diff --git a/bin/zis-plugin-install.sh b/bin/zis-plugin-install.sh index 5ebe86aea..c0028051d 100755 --- a/bin/zis-plugin-install.sh +++ b/bin/zis-plugin-install.sh @@ -1,4 +1,11 @@ #!/bin/sh +# 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. ZIS_PREFIX=${ZIS_PREFIX:-$USER.DEV} ZIS_PARMLIB=${ZIS_PARMLIB:-$ZIS_PREFIX.PARMLIB} @@ -85,3 +92,11 @@ PLUGINID=$2 LMODNAME=`basename $LMODFILE |tr a-z A-Z` add-plugin-to-libs + +# 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/bin/zss-import-jwt-key.sh b/bin/zss-import-jwt-key.sh new file mode 100644 index 000000000..62b3f40f0 --- /dev/null +++ b/bin/zss-import-jwt-key.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# 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. + +mktemp1() +{ + FILE=/tmp/zss-$$-$RANDOM + while [ -e $FILE ] + do + FILE=/tmp/zss-$$-$RANDOM + done + echo $FILE +} + +if [ "$#" -ne 2 ] +then + >&2 cat < +EOF + exit 1 +fi + +SRC_KEYSTORE=$1 +TOKEN=$2 + +>&2 echo "Will import keypair 'jwtsecret' from $SRC_KEYSTORE into $TOKEN" +trap '>&2 echo Failure; exit 1' ERR + +>&2 echo "Checking if $TOKEN exists and its contents..." +if ! /bin/tsocmd "RACDCERT LISTTOKEN('$TOKEN')" +then + /bin/tsocmd "RACDCERT ADDTOKEN('$TOKEN')" +fi + +SRC_KEYSTORE_PASSWORD="${SRC_KEYSTORE_PASSWORD:-password}" +TMP_KEYSTORE_PASSWORD=$SRC_KEYSTORE_PASSWORD + +TMP_KEYSTORE=`mktemp1` +trap 'rm $TMP_KEYSTORE' exit + +>&2 echo "Extracting 'jwtsecret' from $SRC_KEYSTORE..." +keytool -importkeystore \ + -srckeystore $SRC_KEYSTORE -srcstoretype PKCS12 \ + -destkeystore $TMP_KEYSTORE -deststoretype PKCS12 \ + -srcalias jwtsecret -srcstorepass $SRC_KEYSTORE_PASSWORD \ + -deststorepass $TMP_KEYSTORE_PASSWORD + +chmod go-rwx $TMP_KEYSTORE + +>&2 echo "Adding 'jwtsecret' to $TOKEN..." +echo $TMP_KEYSTORE_PASSWORD |gskkyman -i -t $TOKEN -l jwtsecret -p $TMP_KEYSTORE + +>&2 echo "Done" + +# 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/build/zis.xml b/build/zis.xml index b36d82bab..26421f426 100644 --- a/build/zis.xml +++ b/build/zis.xml @@ -58,6 +58,9 @@ + + + @@ -96,6 +99,9 @@ + + + @@ -132,6 +138,9 @@ + + + diff --git a/build/zss.xml b/build/zss.xml index 28a4fb90e..57a2f6ea1 100644 --- a/build/zss.xml +++ b/build/zss.xml @@ -53,6 +53,8 @@ + + @@ -75,6 +77,7 @@ + @@ -85,6 +88,8 @@ + + diff --git a/c/authService.c b/c/authService.c index 49ced1e8d..89de8f3cd 100644 --- a/c/authService.c +++ b/c/authService.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/c/datasetService.c b/c/datasetService.c index efa2de9d6..a3fdb66d6 100644 --- a/c/datasetService.c +++ b/c/datasetService.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #endif diff --git a/c/serviceUtils.c b/c/serviceUtils.c index 653965a1b..78d719a75 100644 --- a/c/serviceUtils.c +++ b/c/serviceUtils.c @@ -26,6 +26,7 @@ #include #include #include +#include #endif #include "zowetypes.h" diff --git a/c/unixFileService.c b/c/unixFileService.c index 004681e7e..e5151515c 100644 --- a/c/unixFileService.c +++ b/c/unixFileService.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "pthread.h" #include "zowetypes.h" diff --git a/c/zis/client.c b/c/zis/client.c index e471ea140..82f2ba5ee 100644 --- a/c/zis/client.c +++ b/c/zis/client.c @@ -237,6 +237,7 @@ const char *ZIS_UPRFSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_UPRFSRV_IMPERSONATION_MISSING] = "Impersonation is required", [RC_ZIS_UPRFSRV_INTERNAL_SERVICE_FAILED] = "R_admin service failed", [RC_ZIS_UPRFSRV_ALLOC_FAILED] = "Alloc function failed", + [RC_ZIS_UPRFSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_UPRFSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -316,7 +317,8 @@ const char *ZIS_GRPRFSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_GRPRFSRV_IMPERSONATION_MISSING] = "Impersonation is required", [RC_ZIS_GRPRFSRV_INTERNAL_SERVICE_FAILED] = "R_admin failed", [RC_ZIS_GRPRFSRV_ALLOC_FAILED] = "Alloc function failed", - [RC_ZIS_GRPRFSRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class is not read" + [RC_ZIS_GRPRFSRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class is not read", + [RC_ZIS_GRPRFSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_GRPRFSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -407,7 +409,8 @@ const char *ZIS_ACSLSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_ACSLSRV_ALLOC_FAILED] = "Alloc function failed", [RC_ZIS_ACSLSRV_INTERNAL_SERVICE_FAILED] = "R_admin failed", [RC_ZIS_ACSLSRV_INSUFFICIENT_SPACE] = "Provided buffer is too small", - [RC_ZIS_ACSLSRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class is not read" + [RC_ZIS_ACSLSRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class is not read", + [RC_ZIS_ACSLSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_ACSLSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -508,7 +511,8 @@ const char *ZIS_GSADMNSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_GSADMNSRV_USER_CLASS_TOO_LONG] = CMS_PROD_ID" class is too long", [RC_ZIS_GSADMNSRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class read failed", [RC_ZIS_GSADMNSRV_AUTOREFRESH_NOT_READ] = "Auto refresh option read failed", - [RC_ZIS_GSADMNSRV_OWNER_TOO_LONG] = "Owner name is too long" + [RC_ZIS_GSADMNSRV_OWNER_TOO_LONG] = "Owner name is too long", + [RC_ZIS_GSADMNSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_GSADMNSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -730,7 +734,8 @@ const char *ZIS_GPPRFSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_GPPRFSRV_PROFILE_COUNT_NULL] = "Profile count is null", [RC_ZIS_GPPRFSRV_IMPERSONATION_MISSING] = "Impersonation is required", [RC_ZIS_GPPRFSRV_INTERNAL_SERVICE_FAILED] = "R_admin failed", - [RC_ZIS_GPPRFSRV_ALLOC_FAILED] = "Alloc function failed" + [RC_ZIS_GPPRFSRV_ALLOC_FAILED] = "Alloc function failed", + [RC_ZIS_GPPRFSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_GPPRFSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -809,7 +814,8 @@ const char *ZIS_GRPALSRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_GRPALSRV_IMPERSONATION_MISSING] = "Impersonation is required", [RC_ZIS_GRPALSRV_ALLOC_FAILED] = "Alloc function failed", [RC_ZIS_GRPALSRV_INTERNAL_SERVICE_FAILED] = "R_admin failed", - [RC_ZIS_GRPALSRV_INSUFFICIENT_SPACE] = "Provided buffer is too small" + [RC_ZIS_GRPALSRV_INSUFFICIENT_SPACE] = "Provided buffer is too small", + [RC_ZIS_GRPALSRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_GRPALSRV_WRAPPER_RC_DESCRIPTION[] = { @@ -898,7 +904,8 @@ const char *ZIS_GRPASRV_SERVICE_RC_DESCRIPTION[] = { [RC_ZIS_GRPASRV_PARM_INTERNAL_ERROR] = "Parm list creation failed", [RC_ZIS_GRPASRV_USER_CLASS_TOO_LONG] = CMS_PROD_ID" class is too long", [RC_ZIS_GRPASRV_USER_CLASS_NOT_READ] = CMS_PROD_ID" class read failed", - [RC_ZIS_GRPASRV_AUTOREFRESH_NOT_READ] = "Auto refresh option read failed" + [RC_ZIS_GRPASRV_AUTOREFRESH_NOT_READ] = "Auto refresh option read failed", + [RC_ZIS_GRPASRV_UNSUPPORTED_ESM] = "ESM not supported" }; const char *ZIS_GRPASRV_WRAPPER_RC_DESCRIPTION[] = { diff --git a/c/zis/services/secmgmt.c b/c/zis/services/secmgmt.c index cdf68d875..ef5b13f16 100644 --- a/c/zis/services/secmgmt.c +++ b/c/zis/services/secmgmt.c @@ -28,26 +28,9 @@ #include "zis/utils.h" #include "zis/services/secmgmt.h" +#include "zis/services/secmgmtUtils.h" -#define ZIS_PARMLIB_PARM_SECMGMT_USER_CLASS CMS_PROD_ID".SECMGMT.CLASS" -#define ZIS_PARMLIB_PARM_SECMGMT_AUTORESFRESH CMS_PROD_ID".SECMGMT.AUTOREFRESH" - -static bool getCallerUserID(RadminUserID *caller) { - - ACEE aceeData = {0}; - ACEE *aceeAddress = NULL; - cmGetCallerTaskACEE(&aceeData, &aceeAddress); - if (aceeAddress == NULL) { - return false; - } - - caller->length = aceeData.aceeuser[0]; - memcpy(caller->value, &aceeData.aceeuser[1], sizeof(caller->value)); - - return true; -} - -static int validateUserProfileParmList(ZISUserProfileServiceParmList *parm) { +int validateUserProfileParmList(ZISUserProfileServiceParmList *parm) { if (memcmp(parm->eyecatcher, ZIS_USERPROF_SERVICE_PARMLIST_EYECATCHER, sizeof(parm->eyecatcher))) { @@ -68,20 +51,22 @@ static int validateUserProfileParmList(ZISUserProfileServiceParmList *parm) { static void copyUserProfiles(ZISUserProfileEntry *dest, const RadminBasicUserPofileInfo *src, unsigned int count) { - + /* RadminBasicUserPofileInfo and ZISUserProfileEntry have different sizes + * for the name, so it must be taken into an account when copying to the + * other address space. + */ for (unsigned int i = 0; i < count; i++) { - cmCopyToSecondaryWithCallerKey(dest[i].userID, src[i].userID, - sizeof(dest[i].userID)); - cmCopyToSecondaryWithCallerKey(dest[i].defaultGroup, src[i].defaultGroup, - sizeof(dest[i].defaultGroup)); - cmCopyToSecondaryWithCallerKey(dest[i].name, src[i].name, - sizeof(dest[i].name)); + ZISUserProfileEntry tmpDest = {0}; + memcpy(tmpDest.userID, src[i].userID, sizeof(src[i].userID)); + memcpy(tmpDest.defaultGroup, src[i].defaultGroup, sizeof(src[i].defaultGroup)); + memcpy(tmpDest.name, src[i].name, sizeof(src[i].name)); + cmCopyToSecondaryWithCallerKey(&dest[i], &tmpDest, sizeof(tmpDest)); } } -int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, +static int zisUserProfilesServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_UPRFSRV_OK; @@ -109,7 +94,7 @@ int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_UPRFSRV_IMPERSONATION_MISSING; break; } @@ -208,7 +193,17 @@ int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } -static int validateGenresProfileParmList(ZISGenresProfileServiceParmList *parm) +int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisUserProfilesServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisUserProfilesServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_UPRFSRV_UNSUPPORTED_ESM; + } +} + +int validateGenresProfileParmList(ZISGenresProfileServiceParmList *parm) { if (memcmp(parm->eyecatcher, ZIS_GRESPROF_SERVICE_PARMLIST_EYECATCHER, @@ -234,18 +229,20 @@ static int validateGenresProfileParmList(ZISGenresProfileServiceParmList *parm) static void copyGenresProfiles(ZISGenresProfileEntry *dest, const RadminBasicGenresPofileInfo *src, unsigned int count) { - + /* RadminBasicGenresPofileInfo and ZISGenresProfileEntry have different sizes + * for the profile, so it must be taken into an account when copying to the + * other address space. + */ for (unsigned int i = 0; i < count; i++) { - cmCopyToSecondaryWithCallerKey(dest[i].profile, src[i].profile, - sizeof(dest[i].profile)); - cmCopyToSecondaryWithCallerKey(dest[i].owner, src[i].owner, - sizeof(dest[i].owner)); + ZISGenresProfileEntry tmpDest = {0}; + memcpy(tmpDest.profile, src[i].profile, sizeof(src[i].profile)); + memcpy(tmpDest.owner, src[i].owner, sizeof(src[i].owner)); + cmCopyToSecondaryWithCallerKey(&dest[i], &tmpDest, sizeof(tmpDest)); } - } -int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGenresProfilesServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_GRPRFSRV_OK; @@ -273,7 +270,7 @@ int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_GRPRFSRV_IMPERSONATION_MISSING; break; } @@ -399,8 +396,17 @@ int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } -static int -validateGenresAccessListParmList(ZISGenresAccessListServiceParmList *parm) { +int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGenresProfilesServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGenresProfilesServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_GRPRFSRV_UNSUPPORTED_ESM; + } +} + +int validateGenresAccessListParmList(ZISGenresAccessListServiceParmList *parm) { if (memcmp(parm->eyecatcher, ZIS_ACSLIST_SERVICE_PARMLIST_EYECATCHER, sizeof(parm->eyecatcher))) { @@ -435,7 +441,7 @@ static void copyGenresAccessList(ZISGenresAccessEntry *dest, } -int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGenresAccessListServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_ACSLSRV_OK; @@ -463,7 +469,7 @@ int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_ACSLSRV_IMPERSONATION_MISSING; break; } @@ -587,7 +593,17 @@ int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } -static int validateGenresParmList(ZISGenresAdminServiceParmList *parmList) { +int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGenresAccessListServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGenresAccessListServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_ACSLSRV_UNSUPPORTED_ESM; + } +} + +int validateGenresParmList(ZISGenresAdminServiceParmList *parmList) { if (memcmp(parmList->eyecatcher, ZIS_GENRES_ADMIN_SERVICE_PARMLIST_EYECATCHER, @@ -913,7 +929,7 @@ static int performRefreshIfNeeded(CrossMemoryServerGlobalArea *globalArea, return RC_ZIS_GSADMNSRV_OK; } -int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGenresProfileAdminServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_GSADMNSRV_OK; @@ -941,7 +957,7 @@ int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_GSADMNSRV_IMPERSONATION_MISSING; break; } @@ -1076,7 +1092,17 @@ int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea return status; } -static int validateGroupProfileParmList(ZISGroupProfileServiceParmList *parm) +int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGenresProfileAdminServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGenresProfileAdminServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_GSADMNSRV_UNSUPPORTED_ESM; + } +} + +int validateGroupProfileParmList(ZISGroupProfileServiceParmList *parm) { if (memcmp(parm->eyecatcher, ZIS_GRPPROF_SERVICE_PARMLIST_EYECATCHER, @@ -1110,7 +1136,7 @@ static void copyGroupProfiles(ZISGroupProfileEntry *dest, } -int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGroupProfilesServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_GPPRFSRV_OK; @@ -1138,7 +1164,7 @@ int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_GPPRFSRV_IMPERSONATION_MISSING; break; } @@ -1240,8 +1266,17 @@ int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } -static int -validateGroupAccessListParmList(ZISGroupAccessListServiceParmList *parm) { +int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGroupProfilesServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGroupProfilesServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_GPPRFSRV_UNSUPPORTED_ESM; + } +} + +int validateGroupAccessListParmList(ZISGroupAccessListServiceParmList *parm) { if (memcmp(parm->eyecatcher, ZIS_GRPALST_SERVICE_PARMLIST_EYECATCHER, sizeof(parm->eyecatcher))) { @@ -1272,7 +1307,7 @@ static void copyGroupAccessList(ZISGroupAccessEntry *dest, } -int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGroupAccessListServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_GRPALSRV_OK; @@ -1300,7 +1335,7 @@ int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_GRPALSRV_IMPERSONATION_MISSING; break; } @@ -1400,7 +1435,17 @@ int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } -static int validateGroupParmList(ZISGroupAdminServiceParmList *parmList) { +int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGroupAccessListServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGroupAccessListServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_GRPALSRV_UNSUPPORTED_ESM; + } +} + +int validateGroupParmList(ZISGroupAdminServiceParmList *parmList) { if (memcmp(parmList->eyecatcher, ZIS_GROUP_ADMIN_SERVICE_PARMLIST_EYECATCHER, @@ -1670,7 +1715,7 @@ static RadminConnectionParmList *constructConnectionAdminParmList( return parmList; } -int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, +int zisGroupAdminServiceFunctionRACF(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm) { int status = RC_ZIS_GRPASRV_OK; @@ -1698,7 +1743,7 @@ int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, do { RadminUserID caller; - if (!getCallerUserID(&caller)) { + if (!secmgmtGetCallerUserID(&caller)) { status = RC_ZIS_GRPASRV_IMPERSONATION_MISSING; break; } @@ -1839,6 +1884,15 @@ int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, return status; } +int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + ExternalSecurityManager esm = getExternalSecurityManager(); + switch (esm) { + case ZOS_ESM_RACF: return zisGroupAdminServiceFunctionRACF(globalArea, service, parm); + case ZOS_ESM_RTSS: return zisGroupAdminServiceFunctionTSS(globalArea, service, parm); + default: return RC_ZIS_GRPASRV_UNSUPPORTED_ESM; + } +} /* This program and the accompanying materials are diff --git a/c/zis/services/secmgmtUtils.c b/c/zis/services/secmgmtUtils.c new file mode 100644 index 000000000..60fe7f174 --- /dev/null +++ b/c/zis/services/secmgmtUtils.c @@ -0,0 +1,50 @@ + + +/* + 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 + +#ifndef METTLE +#error Non-metal C code is not supported +#endif + +#include "zowetypes.h" +#include "crossmemory.h" +#include "radmin.h" +#include "zos.h" + +bool secmgmtGetCallerUserID(RadminUserID *caller) { + + ACEE aceeData = {0}; + ACEE *aceeAddress = NULL; + cmGetCallerTaskACEE(&aceeData, &aceeAddress); + if (aceeAddress == NULL) { + return false; + } + + caller->length = aceeData.aceeuser[0]; + memcpy(caller->value, &aceeData.aceeuser[1], sizeof(caller->value)); + + return true; +} + +/* + 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/zis/services/secmgmttss.c b/c/zis/services/secmgmttss.c new file mode 100644 index 000000000..3d33b482a --- /dev/null +++ b/c/zis/services/secmgmttss.c @@ -0,0 +1,3113 @@ + + +/* + 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 +#include + +#ifndef METTLE +#error Non-metal C code is not supported +#endif + +#include "zowetypes.h" +#include "alloc.h" +#include "crossmemory.h" +#include "radmin.h" +#include "zis/utils.h" +#include "zis/services/secmgmt.h" +#include "zis/services/tssparsing.h" +#include "zis/services/secmgmtUtils.h" + +/* A struct to more easily carry the required arguments + * for logging in ZIS into handler and helper functions. + */ +typedef struct TSSLogData_t { + CrossMemoryServerGlobalArea *globalArea; + int traceLevel; +} TSSLogData; + +/* A helper function which finds the line where the value where we are looking for starts + * and returns a pointer to it. This is useful for when the caller wants to start from + * a particular value because they either reached the maximum they could return in a + * request, or if they want to information on a single user. It also returns pointers + * to the maxOffsetInBlockand the offsetInBlock so when continuing to parse, + * we still have the right place through the output. + */ +static const RadminVLText *findLineOfStart(const RadminVLText *currentLine, + const char *searchKey, + const char *searchValue, + unsigned int searchValueMaxLength, + const unsigned int maxOffsetInBlock, + unsigned int *offsetInBlock, + TSSLogData *logData) { + if (searchValueMaxLength > TSS_MAX_VALUE_LEN) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Maximum value length supplied is %d, but the maximum allowed is %d\n", + searchValueMaxLength, TSS_MAX_VALUE_LEN); + return NULL; + } + + int startValueLen = strlen(searchValue); + CMS_DEBUG2(logData->globalArea, logData->traceLevel, "Length of Search Value: %d\n", startValueLen); + + while (*offsetInBlock < maxOffsetInBlock) { + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + char tmpBuffer[TSS_MAX_VALUE_LEN] = {0}; /* Use the maximum value length of 255 to handle all cases. */ + + if (tssIsOutputDone(currentLine->text, currentLine->length)) { + return NULL; + } + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + + return NULL; + } + else if (numberOfKeys == 0) { + *offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + continue; /* ignore blank lines and carry on */ + } + else { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, "Number of keys on line: %d\n", numberOfKeys); + + int keyIndex = tssFindDesiredKey(searchKey, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, tmpBuffer, searchValueMaxLength); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value for the key, %s...\n", searchKey); + return NULL; + } + + int valueLen = tssGetActualValueLength(tmpBuffer, sizeof(tmpBuffer)); + if (valueLen == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Problem getting the actual length of %.*s\n", sizeof(tmpBuffer), tmpBuffer); + return NULL; + } + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, "Current Value Found: %.*s\n", valueLen, tmpBuffer); + CMS_DEBUG2(logData->globalArea, logData->traceLevel, "Length of Value Found: %d\n", valueLen); + + if (valueLen == startValueLen) { + if (!memcmp(searchValue, tmpBuffer, startValueLen)) { + return currentLine; + } + } + } + } + + *offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + return NULL; +} + +/* Iterates through all the output to find how many access list entries there actually are. This is + * needed by the security endpoint to determine buffer size in case the first size allocated isn'tan + * enough. + */ +static int findNumberOfValidGenreEntries(const RadminVLText *currentLine, unsigned int lastByteOffset, + const char *searchProfile, unsigned int *numberOfEntries, + TSSLogData *logData) { + int offsetInBlock = 16; + + while (offsetInBlock < lastByteOffset) { + if (tssIsOutputDone(currentLine->text, currentLine->length)) { + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Found invalid number of keys on line...\n"); + return -1; + } + + int keyIndex = tssFindDesiredKey(TSS_XAUTH_KEY, keys); + if (keyIndex != -1) { + char tmpBuffer[ZIS_SECURITY_PROFILE_MAX_LENGTH] = {0}; + int acidIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, TSS_ACID_KEY); + if (acidIndex == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Expecting %s, but it was not found on line...\n", + TSS_ACID_KEY); + return -1; + } + + int status = tssFindValueForKey(currentLine->text, acidIndex-1, keys, + keyIndex, numberOfKeys, tmpBuffer, + sizeof(tmpBuffer)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_NAME_KEY); + return -1; + } + + int valueLen = tssGetActualValueLength(tmpBuffer, sizeof(tmpBuffer)); + + int profileLength = strlen(searchProfile); + + if (valueLen == profileLength) { + if (!memcmp(searchProfile, tmpBuffer, valueLen)) { + (*numberOfEntries)++; + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + return 0; +} + +/* Iterates through all the output to find the total number of valid lines of data. */ +static const RadminVLText *getNumberOfLinesOfData(const RadminVLText *currentLine, + unsigned int lastByteOffset, + unsigned int *numberOfLines) { + int offsetInBlock = 16; + + while (offsetInBlock < lastByteOffset) { + if (tssIsOutputDone(currentLine->text, currentLine->length)) { + return NULL; + } + + if (!tssIsLineBlank(currentLine->text, currentLine->length)) { + (*numberOfLines)++; + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + return currentLine; +} + +/* A struct to hold the information + * returned from getAcidsInLine() + */ +typedef struct TSSAcidData_t { + char acid[ZIS_USER_ID_MAX_LENGTH]; + bool isAdmin; +} TSSAcidData; + +/* Parses a passed in line and gets the acids. There should be a maximum of four on each line. It copies them into + * an array which is accessed after the call. + */ +static unsigned int getAcidsInLine(const RadminVLText *currentLine, TSSAcidData *data, TSSLogData *logData, int startPos) { + unsigned int acidCounter = 0; + for (int i = 0; i < 4; i++) { + char tmpBuffer[11] = {0}; /* Acid - 8, Admin Indicator - 3 */ + + /* Line can start with spaces. Make sure we get to the actual output. */ + for (int j = startPos; j < currentLine->length; j++) { + if (currentLine->text[j] != ' ') { + startPos = j; + break; + } + } + + /* Check that we aren't going out of bounds. If we reach end of line, + * copy up to that index. + */ + int copySize = sizeof(tmpBuffer); + if (startPos + copySize > currentLine->length - 1) { + copySize = (currentLine->length - 1) - startPos; + } + + if (copySize <= 0) { + return acidCounter; /* Probably less than four acids on this line... */ + } + + memcpy(tmpBuffer, currentLine->text+startPos, copySize); + + memcpy(data[i].acid, tmpBuffer, sizeof(data[i].acid)); + padWithSpaces(data[i].acid, sizeof(data[i].acid), 1, 1); /* All other values are padded with spaces. */ + + int isAdmin = lastIndexOfString(tmpBuffer, sizeof(tmpBuffer), TSS_ADMIN_INDICATOR); + if (isAdmin == -1) { + data[i].isAdmin = FALSE; + } + else { + data[i].isAdmin = TRUE; + } + +#define ACID_OFFSET 12 + startPos+=ACID_OFFSET; /* This is safe enough. */ + + acidCounter++; + } + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Returning %d acids...\n", acidCounter); + + return acidCounter; +} + + +/* A parameter list passed into the handler function + * for the user profiles service. + */ +typedef struct TSSUserProfileParms_t { + ZISUserProfileEntry *tmpResultBuffer; + unsigned int profilesExtracted; + unsigned int profilesToExtract; + const char *startUserID; + int startIndex; + TSSLogData *logData; +} TSSUserProfileParms; + +/* A handler function that is called after the r_admin command is issued. It parses the user + * data from the output by going line by line. + */ +static int userProfilesHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSUserProfileParms *parms = userData; + ZISUserProfileEntry *buffer = parms->tmpResultBuffer; + TSSLogData *logData = parms->logData; + ZISUserProfileEntry entry = {0}; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + /* If specified, iterate through the output until start is found. If it's not ever found + * then return an error. + */ + unsigned int offsetInBlock = 16; + if (parms->startUserID != NULL) { + do { + currentLine = findLineOfStart(currentLine, TSS_ACCESSOR_ID_KEY, parms->startUserID, + ZIS_USER_ID_MAX_LENGTH, currentBlock->lastByteOffset, + &offsetInBlock, logData); + if (currentLine != NULL) { + break; + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + } + } while (currentBlock != NULL); + + if (currentLine == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Start profile %s could not be found...\n", parms->startUserID); + return 0; + } + } + + /* Iterate through the output and store information on the users that + * are found. + */ + unsigned int numExtracted = parms->startIndex; /* this is for any continuations with multiple calls using the same array */ + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (numExtracted == parms->profilesToExtract || tssIsOutputDone(currentLine->text, currentLine->length)) { + parms->profilesExtracted = numExtracted; + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + return -1; + } + /* A blank line means the end of a single user's data. + * Store what was found into the ZISUserProfileEntry + * struct. + */ + else if (numberOfKeys == 0) { + ZISUserProfileEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + memcpy(p->userID, entry.userID, sizeof(p->userID)); + memcpy(p->name, entry.name, sizeof(p->name)); + memcpy(p->defaultGroup, entry.defaultGroup, sizeof(p->defaultGroup)); + memset(&entry, 0, sizeof(ZISUserProfileEntry)); + } + else { + int keyIndex = tssFindDesiredKey(TSS_ACCESSOR_ID_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, entry.userID, sizeof(entry.userID)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_ACCESSOR_ID_KEY); + return -1; + } + } + + keyIndex = tssFindDesiredKey(TSS_NAME_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, entry.name, sizeof(entry.name)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_NAME_KEY); + return -1; + } + } + + keyIndex = tssFindDesiredKey(TSS_DEFAULT_GROUP_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, keyIndex, + numberOfKeys, entry.defaultGroup, sizeof(entry.defaultGroup)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_DEFAULT_GROUP_KEY); + return -1; + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Prepares to copy the ZISUserProfileEntry struct to the other address space where + * it can be returned to the caller. + */ +static void copyUserProfiles(ZISUserProfileEntry *dest, ZISUserProfileEntry *src, + unsigned int count) { + for (unsigned int i = 0; i < count; i++) { + cmCopyToSecondaryWithCallerKey(dest[i].userID, src[i].userID, + sizeof(dest[i].userID)); + cmCopyToSecondaryWithCallerKey(dest[i].defaultGroup, src[i].defaultGroup, + sizeof(dest[i].defaultGroup)); + cmCopyToSecondaryWithCallerKey(dest[i].name, src[i].name, + sizeof(dest[i].name)); + } +} + +/* Goes through several r_admin commands to retrieve information on all the users + * in the system, including administrators. + */ +static int getAllUsers(RadminStatus *radminStatus, RadminCallerAuthInfo authInfo, + TSSUserProfileParms *parms) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract USER profiles...\n"); + + int radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(USER)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract USER profiles...\n"); + return radminExtractRC; + } + + parms->startIndex = parms->profilesExtracted; + if (parms->startIndex >= parms->profilesToExtract) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Stopping extraction of DCA profiles because the buffer would overflow...\n"); + return RC_RADMIN_OK; + } + + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract DCA profiles...\n"); + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(DCA)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract DCA profiles...\n"); + return radminExtractRC; + } + + parms->startIndex = parms->profilesExtracted; + if (parms->startIndex >= parms->profilesToExtract) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Stopping extraction of VCA profiles because the buffer would overflow...\n"); + return RC_RADMIN_OK; + } + + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract VCA profiles...\n"); + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(VCA)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract VCA profiles...\n"); + return radminExtractRC; + } + + parms->startIndex = parms->profilesExtracted; + if (parms->startIndex >= parms->profilesToExtract) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Stopping extraction of ZCA profiles because the buffer would overflow...\n"); + return RC_RADMIN_OK; + } + + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract ZCA profiles...\n"); + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(ZCA)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract ZCA profiles...\n"); + return radminExtractRC; + } + + parms->startIndex = parms->profilesExtracted; + if (parms->startIndex >= parms->profilesToExtract) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Stopping extraction of LSCA profiles because the buffer would overflow...\n"); + return RC_RADMIN_OK; + } + + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract LSCA profiles...\n"); + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(LSCA)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract LSCA profiles...\n"); + return radminExtractRC; + } + + parms->startIndex = parms->profilesExtracted; + if (parms->startIndex >= parms->profilesToExtract) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Stopping extraction of SCA profiles because the buffer would overflow...\n"); + return RC_RADMIN_OK; + } + + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "About to extract SCA profiles...\n"); + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(SCA)", + userProfilesHandler, + parms, + radminStatus + ); + + if (radminExtractRC != RC_RADMIN_OK) { + CMS_DEBUG2(parms->logData->globalArea, parms->logData->traceLevel, + "Could not extract SCA profiles...\n"); + return radminExtractRC; + } + + return RC_RADMIN_OK; +} + +/* The entry point into the user profiles endpoint logic. */ +int zisUserProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_UPRFSRV_OK; + int radminExtractRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_UPRFSRV_PARMLIST_NULL; + } + + ZISUserProfileServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, sizeof(localParmList)); + + int parmValidateRC = validateUserProfileParmList(&localParmList); + if (parmValidateRC != RC_ZIS_UPRFSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_UPRFSRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, "UPRFSRV: userID = \'%.*s\', " + "profilesToExtract = %u, result buffer = 0x%p\n", + localParmList.startUserID.length, + localParmList.startUserID.value, + localParmList.profilesToExtract, + localParmList.resultBuffer); + CMS_DEBUG2(globalArea, traceLevel, "UPRFSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + char userIDBuffer[ZIS_USER_ID_MAX_LENGTH + 1] = {0}; + memcpy(userIDBuffer, localParmList.startUserID.value, + localParmList.startUserID.length); + const char *startUserIDNullTerm = localParmList.startUserID.length > 0 ? + userIDBuffer : NULL; + + size_t tmpResultBufferSize = + sizeof(ZISUserProfileEntry) * localParmList.profilesToExtract; + int allocRC = 0, allocSysRC = 0, allocSysRSN = 0; + ZISUserProfileEntry *tmpResultBuffer = + (ZISUserProfileEntry *)safeMalloc64v3(tmpResultBufferSize, TRUE, + "ZISUserProfileEntry", + &allocRC, + &allocSysRSN, + &allocSysRSN); + + CMS_DEBUG2(globalArea, traceLevel, + "UPRFSRV: tmpBuff allocated @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + allocRC, allocSysRC, allocSysRSN); + + if (tmpResultBuffer == NULL) { + return RC_ZIS_UPRFSRV_ALLOC_FAILED; + } + + RadminStatus radminStatus = {0}; + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + TSSUserProfileParms parms = { + .profilesToExtract = localParmList.profilesToExtract, + .tmpResultBuffer = tmpResultBuffer, + .startUserID = startUserIDNullTerm, + .logData = &logData, + .startIndex = 0 + }; + + radminExtractRC = getAllUsers(&radminStatus, authInfo, &parms); + + CMS_DEBUG2(globalArea, traceLevel, + "Extracted user %d profiles...\n", parms.profilesExtracted); + + /* If the system has no users, there must have been a serious + * parsing error somewhere. Return some sort of error to prevent + * having an empty array. Note, even if having no users was correct, + * returning an empty array is not. + */ + if (parms.profilesExtracted == 0 && radminExtractRC == RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, "No user profiles could be found...\n"); + radminExtractRC = RC_RADMIN_NONZERO_USER_RC; + } + + if (radminExtractRC == RC_RADMIN_OK) { + copyUserProfiles(localParmList.resultBuffer, tmpResultBuffer, + parms.profilesExtracted); + localParmList.profilesExtracted = parms.profilesExtracted; + } else { + localParmList.internalServiceRC = radminExtractRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + status = RC_ZIS_UPRFSRV_INTERNAL_SERVICE_FAILED; + } + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISUserProfileServiceParmList)); + + int freeRC = 0, freeSysRC = 0, freeSysRSN = 0; + freeRC = safeFree64v3(tmpResultBuffer, tmpResultBufferSize, + &freeSysRC, &freeSysRSN); + + CMS_DEBUG2(globalArea, traceLevel, + "UPRFSRV: tmpBuff free'd @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + freeRC, freeSysRC, freeSysRSN); + + tmpResultBuffer = NULL; + + return status; +} + +/* A parameter list passed into the handler function + * for the group profiles service. + */ +typedef struct TSSGroupProfileParms_t { + ZISGroupProfileEntry *tmpResultBuffer; + unsigned int profilesExtracted; + unsigned int profilesToExtract; + const char *startGroupProfile; + TSSLogData *logData; +} TSSGroupProfileParms; + +/* A handler function that is called after the r_admin command is issued. It parses the group + * data from the output by going line by line. + */ +static int groupProfilesHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSGroupProfileParms *parms = userData; + ZISGroupProfileEntry *buffer = parms->tmpResultBuffer; + TSSLogData *logData = parms->logData; + ZISGroupProfileEntry entry = {0}; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + /* If specified, iterate through the output until start is found. If it's not ever found + * then return an error. + */ + unsigned int offsetInBlock = 16; + if (parms->startGroupProfile != NULL) { + do { + currentLine = findLineOfStart(currentLine, TSS_ACCESSOR_ID_KEY, parms->startGroupProfile, + ZIS_SECURITY_GROUP_MAX_LENGTH, currentBlock->lastByteOffset, + &offsetInBlock, logData); + if (currentLine != NULL) { + break; + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + } + } while (currentBlock != NULL); + + if (currentLine == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Start profile %s could not be found...\n", parms->startGroupProfile); + return 0; + } + } + + /* Iterate through the output and store information on the users that + * are found. + */ + unsigned int numExtracted = 0; + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (numExtracted == parms->profilesToExtract || tssIsOutputDone(currentLine->text, currentLine->length)) { + parms->profilesExtracted = numExtracted; + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + return -1; + } + /* A blank line means the end of a single groups's data. + * Store what was found into the ZISGroupProfileEntry + * struct. + */ + else if (numberOfKeys == 0) { + ZISGroupProfileEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + memcpy(p->group, entry.group, sizeof(p->group)); + + memcpy(p->owner, entry.owner, sizeof(p->owner)); + memcpy(p->superiorGroup, entry.superiorGroup, sizeof(p->superiorGroup)); + + memset(&entry, 0, sizeof(ZISGroupProfileEntry)); + } + else { + int keyIndex = tssFindDesiredKey(TSS_ACCESSOR_ID_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, entry.group, sizeof(entry.group)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_ACCESSOR_ID_KEY); + return -1; + } + } + + keyIndex = tssFindDesiredKey(TSS_DEPARTMENT_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, entry.owner, sizeof(entry.owner)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_DEPARTMENT_KEY); + return -1; + } + memcpy(entry.superiorGroup, entry.owner, sizeof(entry.superiorGroup)); /* No superior group in Top Secret. */ + } + + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Prepares to copy the ZISGroupProfileEntry struct to the other address space where + * it can be returned to the caller. + */ +static void copyGroupProfiles(ZISGroupProfileEntry *dest, + ZISGroupProfileEntry *src, + unsigned int count) { + for (unsigned int i = 0; i < count; i++) { + cmCopyToSecondaryWithCallerKey(dest[i].group, src[i].group, + sizeof(dest[i].group)); + cmCopyToSecondaryWithCallerKey(dest[i].owner, src[i].owner, + sizeof(dest[i].owner)); + cmCopyToSecondaryWithCallerKey(dest[i].superiorGroup, src[i].superiorGroup, + sizeof(dest[i].superiorGroup)); + } +} + +/* The entry point into the group profiles endpoint logic. Note, that profile is actually the + * equivalent to a groups in RACF (not to be confused with general resource profile). */ +int zisGroupProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_GPPRFSRV_OK; + int radminExtractRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_GPPRFSRV_PARMLIST_NULL; + } + + ZISGroupProfileServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGroupProfileParmList(&localParmList); + if (parmValidateRC != RC_ZIS_GPPRFSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_GPPRFSRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, + "GPPRFSRV: group = \'%.*s\', profilesToExtract = %u," + " result buffer = 0x%p\n", + localParmList.startGroup.length, + localParmList.startGroup.value, + localParmList.profilesToExtract, + localParmList.resultBuffer); + CMS_DEBUG2(globalArea, traceLevel, "GPPRFSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + char groupBuffer[ZIS_SECURITY_GROUP_MAX_LENGTH + 1] = {0}; + memcpy(groupBuffer, localParmList.startGroup.value, + localParmList.startGroup.length); + const char *startGroupNullTerm = localParmList.startGroup.length > 0 ? + groupBuffer : NULL; + + size_t tmpResultBufferSize = + sizeof(ZISGroupProfileEntry) * localParmList.profilesToExtract; + int allocRC = 0, allocSysRC = 0, allocSysRSN = 0; + ZISGroupProfileEntry *tmpResultBuffer = + (ZISGroupProfileEntry *)safeMalloc64v3( + tmpResultBufferSize, TRUE, + "ZISGroupProfileEntry", + &allocRC, + &allocSysRSN, + &allocSysRSN + ); + + CMS_DEBUG2(globalArea, traceLevel, + "GPPRFSRV: tmpBuff allocated @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + allocRC, allocSysRC, allocSysRSN); + + if (tmpResultBuffer == NULL) { + return RC_ZIS_GPPRFSRV_ALLOC_FAILED; + } + + RadminStatus radminStatus = {0}; + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + TSSGroupProfileParms parms = { + .profilesToExtract = localParmList.profilesToExtract, + .tmpResultBuffer = tmpResultBuffer, + .startGroupProfile = startGroupNullTerm, + .logData = &logData, + }; + + radminExtractRC = radminRunRACFCommand( + authInfo, + "TSS LIST(ACIDS) TYPE(PROFILE)", + groupProfilesHandler, + &parms, + &radminStatus + ); + + CMS_DEBUG2(globalArea, traceLevel, + "Extracted %d group profiles...\n", parms.profilesExtracted); + + /* If the system has no groups, there must have been a serious + * parsing error somewhere. Return some sort of error to prevent + * having an empty array. Note, even if having no groups was correct, + * returning an empty array is not. + */ + if (parms.profilesExtracted == 0 && radminExtractRC == RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, "No group profiles could be found...\n"); + radminExtractRC = RC_RADMIN_NONZERO_USER_RC; + } + + if (radminExtractRC == RC_RADMIN_OK) { + copyGroupProfiles(localParmList.resultBuffer, tmpResultBuffer, + parms.profilesExtracted); + localParmList.profilesExtracted = parms.profilesExtracted; + } + else { + localParmList.internalServiceRC = radminExtractRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + status = RC_ZIS_GPPRFSRV_INTERNAL_SERVICE_FAILED; + } + + int freeRC = 0, freeSysRC = 0, freeSysRSN = 0; + freeRC = safeFree64v3(tmpResultBuffer, tmpResultBufferSize, + &freeSysRC, &freeSysRSN); + + CMS_DEBUG2(globalArea, traceLevel, + "GPPRFSRV: tmpBuff free'd @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + freeRC, freeSysRC, freeSysRSN); + + tmpResultBuffer = NULL; + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGroupProfileServiceParmList)); + + return status; +} + +/* A parameter list passed into the handler function + * for the genres profile access list extraction service. + */ +typedef struct TSSGenresProfileAccessListParms_t { + ZISGenresAccessEntry *tmpResultBuffer; + unsigned int entriesExtracted; + unsigned int entriesFound; + unsigned int entriesToExtract; + char *searchProfile; + TSSLogData *logData; +} TSSGenresProfileAccessListParms; + +/* A handler function that is called after the r_admin command is issued. It parses the access list + * data from the output by going line by line. + */ +static int genresProfileAccessListHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSGenresProfileAccessListParms *parms = userData; + ZISGenresAccessEntry *buffer = parms->tmpResultBuffer; + TSSLogData *logData = parms->logData; + ZISGenresAccessEntry entry = {0}; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Finding number of valid entries...\n"); + + unsigned int numberOfEntries = 0; + do { + int status = findNumberOfValidGenreEntries(currentLine, currentBlock->lastByteOffset, + parms->searchProfile, &numberOfEntries, logData); + + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "There was an error finding the valid genre entries...\n", + numberOfEntries); + return -1; + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + } + } while (currentBlock != NULL); + + if (numberOfEntries == 0) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No valid entries were found...\n"); + return -1; + } + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Found %d valid entries...\n", numberOfEntries); + + parms->entriesFound = numberOfEntries; + + currentBlock = result; /* Bring us back to the beginning. */ + currentLine = ¤tBlock->firstMessageEntry; + + unsigned int offsetInBlock = 16; + + /* Iterate through the output and store information on the access list that + * are found. + */ + unsigned int numExtracted = 0; + bool foundProfile = FALSE; /* Keeps track of which access level goes to which user */ + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (numExtracted == parms->entriesToExtract || tssIsOutputDone(currentLine->text, currentLine->length)) { + parms->entriesExtracted = numExtracted; + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1 || numberOfKeys == 0) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + return -1; + } + else { + int keyIndex = tssFindDesiredKey(TSS_XAUTH_KEY, keys); + if (keyIndex != -1) { + char tmpBuffer[ZIS_SECURITY_PROFILE_MAX_LENGTH] = {0}; + int acidIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, TSS_ACID_KEY); + if (acidIndex == -1) { + return -1; + } + + int status = tssFindValueForKey(currentLine->text, acidIndex-1, keys, + keyIndex, numberOfKeys, tmpBuffer, + sizeof(tmpBuffer)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_XAUTH_KEY); + return -1; + } + + int valueLen = tssGetActualValueLength(tmpBuffer, sizeof(tmpBuffer)); + + int profileLength = strlen(parms->searchProfile); + + if (valueLen == profileLength) { + if (!memcmp(tmpBuffer, parms->searchProfile, profileLength)) { + memcpy(entry.id, currentLine->text+acidIndex+5, sizeof(entry.id)); + + foundProfile = TRUE; /* Wait for next access type. */ + } + } + } + else { + if (foundProfile) { + keyIndex = tssFindDesiredKey(TSS_ACCESS_KEY, keys); + if (keyIndex != -1) { + int status = tssFindValueForKey(currentLine->text, currentLine->length, keys, + keyIndex, numberOfKeys, entry.accessType, + sizeof(entry.accessType)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_ACCESS_KEY); + return -1; + } + + ZISGenresAccessEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + + memcpy(p->id, entry.id, sizeof(p->id)); + + + if (!memcmp(entry.accessType, "ALL", 3)) { /* Top Secret doesn't have ALL. */ + memset(entry.accessType, 0, sizeof(entry.accessType)); + memcpy(entry.accessType, "ALTER", 5); + padWithSpaces(entry.accessType, sizeof(entry.accessType), 1, 1); + } + + + memcpy(p->accessType, entry.accessType, sizeof(p->accessType)); + + memset(&entry, 0, sizeof(ZISGenresAccessEntry)); + + foundProfile = FALSE; /* Wait for next profile entry. */ + } + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Prepares to copy the ZISGenresAccessEntry struct to the other address space where + * it can be returned to the caller. + */ +static void copyGenresAccessList(ZISGenresAccessEntry *dest, + const ZISGenresAccessEntry *src, + unsigned int count) { + for (unsigned int i = 0; i < count; i++) { + cmCopyToSecondaryWithCallerKey(dest[i].id, src[i].id, + sizeof(dest[i].id)); + cmCopyToSecondaryWithCallerKey(dest[i].accessType, src[i].accessType, + sizeof(dest[i].accessType)); + } +} + +/* The entry point into the genres profiles endpoint logic. Note, access lists aren't + * stored in the profile like in RACF, instead the user has it. It makes this process + * much more annoying to find the access list entries because of it. + */ +int zisGenresAccessListServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_ACSLSRV_OK; + int radminExtractRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_ACSLSRV_PARMLIST_NULL; + } + + ZISGenresAccessListServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGenresAccessListParmList(&localParmList); + if (parmValidateRC != RC_ZIS_ACSLSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_ACSLSRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, + "ACSLSRV: class = \'%.*s\', result buffer = 0x%p (%u)\n", + localParmList.class.length, localParmList.class.value, + localParmList.resultBuffer, + localParmList.resultBufferCapacity); + CMS_DEBUG2(globalArea, traceLevel, "ACSLSRV: profile = \'%.*s\'\n", + localParmList.profile.length, localParmList.profile.value); + CMS_DEBUG2(globalArea, traceLevel, "ACSLSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + char classNullTerm[ZIS_SECURITY_CLASS_MAX_LENGTH + 1] = {0}; + memcpy(classNullTerm, localParmList.class.value, + localParmList.class.length); + char profileNullTerm[ZIS_SECURITY_PROFILE_MAX_LENGTH + 1] = {0}; + memcpy(profileNullTerm, localParmList.profile.value, + localParmList.profile.length); + + if (localParmList.class.length == 0) { + CrossMemoryServerConfigParm userClassParm = {0}; + int getParmRC = cmsGetConfigParm(&globalArea->serverName, + ZIS_PARMLIB_PARM_SECMGMT_USER_CLASS, + &userClassParm); + if (getParmRC != RC_CMS_OK) { + localParmList.internalServiceRC = getParmRC; + return RC_ZIS_ACSLSRV_USER_CLASS_NOT_READ; + } + if (userClassParm.valueLength > ZIS_SECURITY_CLASS_MAX_LENGTH) { + return RC_ZIS_ACSLSRV_CLASS_TOO_LONG; + } + memcpy(classNullTerm, userClassParm.charValueNullTerm, + userClassParm.valueLength); + } + + size_t tmpResultBufferSize = + sizeof(ZISGenresAccessEntry) * localParmList.resultBufferCapacity; + int allocRC = 0, allocSysRC = 0, allocSysRSN = 0; + ZISGenresAccessEntry *tmpResultBuffer = + (ZISGenresAccessEntry *)safeMalloc64v3(tmpResultBufferSize, TRUE, + "ZISGenresAccessEntry", + &allocRC, + &allocSysRC, + &allocSysRSN); + CMS_DEBUG2(globalArea, traceLevel, + "ACSLSRV: tmpBuff allocated @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + allocRC, allocSysRC, allocSysRSN); + + if (tmpResultBuffer == NULL) { + return RC_ZIS_ACSLSRV_ALLOC_FAILED; + } + + RadminStatus radminStatus = {0}; + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + TSSGenresProfileAccessListParms parms = { + .tmpResultBuffer = tmpResultBuffer, + .logData = &logData, + .entriesToExtract = localParmList.resultBufferCapacity, + .searchProfile = profileNullTerm, + }; + + CMS_DEBUG2(globalArea, traceLevel, "ResultBufferCapacity: %d\n", + localParmList.resultBufferCapacity); + +#define TSS_PRF_ACCESS_LIST_EXTR_MAX_LEN 274 /* Class - 8, Profile - 255, Rest of command - 11 */ + char extractGenresProfileAccessListCommand[TSS_PRF_ACCESS_LIST_EXTR_MAX_LEN + 1] = {0}; + snprintf(extractGenresProfileAccessListCommand, sizeof(extractGenresProfileAccessListCommand), + "TSS WHOH %s(%s)", classNullTerm, profileNullTerm); + + radminExtractRC = radminRunRACFCommand( + authInfo, + extractGenresProfileAccessListCommand, + genresProfileAccessListHandler, + &parms, + &radminStatus + ); + + if (radminExtractRC == RC_RADMIN_OK) { + copyGenresAccessList(localParmList.resultBuffer, tmpResultBuffer, + parms.entriesExtracted); + localParmList.entriesExtracted = parms.entriesExtracted; + localParmList.entriesFound = parms.entriesExtracted; + } else if (radminExtractRC == RC_RADMIN_OK && parms.entriesExtracted < parms.entriesFound) { + localParmList.entriesExtracted = 0; + localParmList.entriesFound = parms.entriesFound; + status = RC_ZIS_ACSLSRV_INSUFFICIENT_SPACE; + } else { + localParmList.internalServiceRC = radminExtractRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + status = RC_ZIS_ACSLSRV_INTERNAL_SERVICE_FAILED; + } + + int freeRC = 0, freeSysRC = 0, freeSysRSN = 0; + freeRC = safeFree64v3(tmpResultBuffer, tmpResultBufferSize, + &freeSysRC, &freeSysRSN); + CMS_DEBUG2(globalArea, traceLevel, + "ACSLSRV: tmpBuff free'd @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + freeRC, freeSysRC, freeSysRSN); + + tmpResultBuffer = NULL; + + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGenresAccessListServiceParmList)); + + CMS_DEBUG2(globalArea, traceLevel, + "ACSLSRV: entriesExtracted = %u, RC = %d, " + "internal RC = %d, RSN = %d, " + "SAF RC = %d, RACF RC = %d, RSN = %d\n", + localParmList.entriesExtracted, radminExtractRC, + localParmList.internalServiceRC, + localParmList.internalServiceRSN, + localParmList.safStatus.safRC, + localParmList.safStatus.racfRC, + localParmList.safStatus.racfRSN); + + return status; +} + +/* A parameter list passed into the handler function + * for the group access list extraction service. + */ +typedef struct TSSGroupAccessListParms_t { + ZISGroupAccessEntry *tmpResultBuffer; + int entriesExtracted; + int entriesFound; + int entriesToExtract; + TSSLogData *logData; +} TSSGroupAccessListParms; + +/* A handler function that is called after the r_admin command is issued. It parses the group access list + * data from the output by going line by line. + */ +static int groupAccessListHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSGroupAccessListParms *parms = userData; + ZISGroupAccessEntry *buffer = parms->tmpResultBuffer; + TSSLogData *logData = parms->logData; + ZISGroupAccessEntry entry = {0}; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); /* go to next line */ + + unsigned int numberOfLines = 0; + do { + currentLine = getNumberOfLinesOfData(currentLine, currentBlock->lastByteOffset, + &numberOfLines); + + if (currentLine == NULL) { + break; + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = ¤tBlock->firstMessageEntry; + } + } while (currentBlock != NULL); + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Found %d total lines of data...\n", numberOfLines); + + parms->entriesFound = numberOfLines * 4 ; /* four entries max on each line */ + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Total number of users in group estimated at %d...\n", + parms->entriesFound); + + currentBlock = result; /* point back to first block */ + currentLine = ¤tBlock->firstMessageEntry; /* point to start of output */ + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); /* go to next line */ + + int acidsIndex = indexOfString((char *)currentLine->text, currentLine->length, TSS_ACIDS_KEY, 0); + if (acidsIndex == -1) { + return -1; /* not expected */ + } + + int delimIndex = indexOfString((char *)currentLine->text, currentLine->length, TSS_KEY_DELIMITER, 0); + if (delimIndex == -1) { + return -1; + } + + unsigned int offsetInBlock = 16; + + /* Iterate through the output and store information on the users that + * are found. + */ + unsigned int numExtracted = 0; + + bool firstLoop = TRUE; + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (numExtracted == parms->entriesToExtract || tssIsOutputDone(currentLine->text, currentLine->length)) { + parms->entriesExtracted = numExtracted; + return 0; + } + + if (!tssIsLineBlank(currentLine->text, currentLine->length)) { + TSSAcidData acids[4] = {0}; + unsigned int acidsFound = 0; + if (firstLoop) { + acidsFound = getAcidsInLine(currentLine, acids, logData, delimIndex+TSS_KEY_DELIMITER_LEN); + firstLoop = FALSE; + } + else { + acidsFound = getAcidsInLine(currentLine, acids, logData, 0); + } + if (acidsFound == 0 || acidsFound > 4) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Invalid number of acids found...\n"); + return -1; + } + + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "%d acids were found on line: %s\n", acidsFound); + + + for (int i = 0; i < acidsFound; i++) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "ACID %d: %.*s\n", acidsFound, sizeof(acids[i].acid), acids[i].acid); + + ZISGroupAccessEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + + memcpy(p->id, acids[i].acid, sizeof(p->id)); + + if (acids[i].isAdmin) { + memcpy(p->accessType, "JOIN", sizeof(p->accessType)); + } + else { + memcpy(p->accessType, "USE", sizeof(p->accessType)); + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Prepares to copy the ZISGroupAccessEntry struct to the other address space where + * it can be returned to the caller. + */ +static void copyGroupAccessList(ZISGroupAccessEntry *dest, + const ZISGroupAccessEntry *src, + unsigned int count) { + + for (unsigned int i = 0; i < count; i++) { + cmCopyToSecondaryWithCallerKey(dest[i].id, src[i].id, + sizeof(dest[i].id)); + cmCopyToSecondaryWithCallerKey(dest[i].accessType, src[i].accessType, + sizeof(dest[i].accessType)); + } +} + +/* The entry point into the group access list endpoint logic. */ +int zisGroupAccessListServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_GRPALSRV_OK; + int radminExtractRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_GRPALSRV_PARMLIST_NULL; + } + + ZISGroupAccessListServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGroupAccessListParmList(&localParmList); + if (parmValidateRC != RC_ZIS_GRPALSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_GRPALSRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, + "GRPALSRV: group = \'%.*s\', result buffer = 0x%p (%u)\n", + localParmList.group.length, localParmList.group.value, + localParmList.resultBuffer, + localParmList.resultBufferCapacity); + CMS_DEBUG2(globalArea, traceLevel, "GRPALSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + char groupNameNullTerm[ZIS_SECURITY_GROUP_MAX_LENGTH + 1] = {0}; + memcpy(groupNameNullTerm, localParmList.group.value, + localParmList.group.length); + + size_t tmpResultBufferSize = + sizeof(ZISGroupAccessEntry) * localParmList.resultBufferCapacity; + int allocRC = 0, allocSysRC = 0, allocSysRSN = 0; + ZISGroupAccessEntry *tmpResultBuffer = + (ZISGroupAccessEntry *)safeMalloc64v3(tmpResultBufferSize, TRUE, + "RadminGroupAccessListEntry", + &allocRC, + &allocSysRC, + &allocSysRSN); + CMS_DEBUG2(globalArea, traceLevel, + "GRPALSRV: tmpBuff allocated @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + allocRC, allocSysRC, allocSysRSN); + + if (tmpResultBuffer == NULL) { + return RC_ZIS_GRPALSRV_ALLOC_FAILED; + } + + RadminStatus radminStatus = {0}; + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + TSSGroupAccessListParms parms = { + .tmpResultBuffer = tmpResultBuffer, + .logData = &logData, + .entriesToExtract = localParmList.resultBufferCapacity, + }; + + CMS_DEBUG2(globalArea, traceLevel, "ResultBufferCapacity: %d\n", + localParmList.resultBufferCapacity); + +#define TSS_GRP_ACCESS_LIST_EXTR_MAX_LEN 30 /* ACID - 8, Rest of command - 22 */ + char extractGroupAccessList[TSS_GRP_ACCESS_LIST_EXTR_MAX_LEN + 1] = {0}; + snprintf(extractGroupAccessList, sizeof(extractGroupAccessList), "TSS LIST(%s) DATA(ACIDS)", groupNameNullTerm); + + radminExtractRC = radminRunRACFCommand( + authInfo, + extractGroupAccessList, + groupAccessListHandler, + &parms, + &radminStatus + ); + + CMS_DEBUG2(globalArea, traceLevel, "Entries Extracted: %d\n", + parms.entriesExtracted); + + CMS_DEBUG2(globalArea, traceLevel, "Entries Found: %d\n", + parms.entriesFound); + + int entryDifference = parms.entriesFound - parms.entriesExtracted; + + if (radminExtractRC == RC_RADMIN_OK && entryDifference > 4) { + localParmList.entriesExtracted = 0; + localParmList.entriesFound = parms.entriesFound; + status = RC_ZIS_GRPALSRV_INSUFFICIENT_SPACE; + } + else if (radminExtractRC == RC_RADMIN_OK && entryDifference <= 4) { + copyGroupAccessList(localParmList.resultBuffer, tmpResultBuffer, + parms.entriesExtracted); + localParmList.entriesExtracted = parms.entriesExtracted; + localParmList.entriesFound = parms.entriesExtracted; + } + else { + localParmList.internalServiceRC = radminExtractRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + status = RC_ZIS_GRPALSRV_INTERNAL_SERVICE_FAILED; + } + + int freeRC = 0, freeSysRC = 0, freeSysRSN = 0; + freeRC = safeFree64v3(tmpResultBuffer, tmpResultBufferSize, + &freeSysRC, &freeSysRSN); + + CMS_DEBUG2(globalArea, traceLevel, + "GPPRFSRV: tmpBuff free'd @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + freeRC, freeSysRC, freeSysRSN); + + tmpResultBuffer = NULL; + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGroupProfileServiceParmList)); + + return status; +} + +/* After issuing a command, TSS will spit out + * a reason code if there is an error, or a + * messageid. + */ +typedef struct TSSCommandOutput_t { + int returnCode; + char reasonMessageID[TSS_MESSAGE_ID_MAX_LEN]; +} TSSCommandOutput; + +/* This function returns TRUE on a command failure or FALSE + * on a command success. + */ +static bool didCommandFail(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + + if (status.racfRC != 0) { + return TRUE; + } + + TSSCommandOutput *output = userData; + + const RadminCommandOutput *currentBlock = result; + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + int offsetInBlock = 16; + bool commandFailure = FALSE; + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (currentLine->length >= TSS_MESSAGE_ID_MAX_LEN) { + if (!memcmp(currentLine->text, TSS_COMMAND_SUCCESS, strlen(TSS_COMMAND_SUCCESS))) { + output->returnCode = 0; + return commandFailure; + } + if (!memcmp(currentLine->text, TSS_COMMAND_FAILURE, strlen(TSS_COMMAND_FAILURE))) { + int retCodeIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, "RETURN CODE"); + int equalSignIndex = indexOfString((char*)currentLine->text, currentLine->length, "=", retCodeIndex); + + int valueIndex = equalSignIndex; + while (valueIndex <= currentLine->length - 1 && valueIndex >= 0) { + if (isdigit(currentLine->text[valueIndex])) { + char c1 = currentLine->text[valueIndex]; + char c2 = '\0'; + + if (valueIndex + 1 <= currentLine->length - 1) { + if (isdigit(currentLine->text[valueIndex + 1])) { + c2 = currentLine->text[valueIndex + 1]; + } + } + + char returnCodeString[3] = { c1, c2, '\0'}; + + int returnCode = atoi(returnCodeString); + output->returnCode = returnCode; + + break; + } + + valueIndex++; + } + + commandFailure = TRUE; + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = &(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + currentBlock = result; + currentLine = (RadminVLText *)¤tBlock->firstMessageEntry; + + if (currentLine->length >= TSS_MESSAGE_ID_MAX_LEN) { + if (!memcmp(currentLine->text, TSS_FUNCTION_END, strlen(TSS_FUNCTION_END))) { + memcpy(output->reasonMessageID, currentLine->text, sizeof(output->reasonMessageID)); + } + } + + return commandFailure; +} + +/* Executes an admin command */ +static int executeAdminCommand(RadminStatus *radminStatus, + char *operatorCommand, char *serviceMessage, + int serviceMessageLength, + RadminCallerAuthInfo authInfo) { + int radminActionRC = RC_RADMIN_OK; + + TSSCommandOutput output = {0}; + + radminActionRC = radminRunRACFCommand( + authInfo, + operatorCommand, + didCommandFail, + &output, + radminStatus + ); + + if (radminActionRC != RC_RADMIN_OK) { + if (serviceMessageLength >= TSS_MESSAGE_ID_MAX_LEN) { + snprintf(serviceMessage, serviceMessageLength, "%.*s, RC = %d", sizeof(output.reasonMessageID), + output.reasonMessageID, output.returnCode); + } + } + + return radminActionRC; +} + +/* The entry point into the group admin endpoint logic. */ +int zisGroupAdminServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_GRPASRV_OK; + int radminActionRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_GRPASRV_PARMLIST_NULL; + } + + ZISGroupAdminServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGroupParmList(&localParmList); + if (parmValidateRC != RC_ZIS_GRPASRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_GRPASRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, + "GRPASRV: flags = 0x%X, function = %d, group = \'%.*s\', " + "superior group = \'%.*s\', user = \'%.*s\', access = %d'\n", + localParmList.flags, + localParmList.function, + localParmList.group.length, localParmList.group.value, + localParmList.superiorGroup.length, + localParmList.superiorGroup.value, + localParmList.user.length, localParmList.user.value, + localParmList.accessType); + CMS_DEBUG2(globalArea, traceLevel, "GRPASRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + bool isDryRun = (localParmList.flags & ZIS_GROUP_ADMIN_FLAG_DRYRUN) ? + true : false; + + RadminStatus radminStatus = {0}; + + char serviceMessage[TSS_SERVICE_MSG_MAX_LEN + 1] = {0}; + + if (localParmList.function == ZIS_GROUP_ADMIN_SRV_FC_ADD) { +#define TSS_GRP_ADMIN_ADD_MAX_LEN 94 /* ACID - 8, Name - 32, Department - 8, Rest of command - 46 */ + char operatorCommand[TSS_GRP_ADMIN_ADD_MAX_LEN + 1] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), + "TSS CREATE(%.*s) NAME('%.*s') TYPE(PROFILE) DEPARTMENT(%.*s)", + localParmList.group.length, localParmList.group.value, + localParmList.group.length, localParmList.group.value, + localParmList.superiorGroup.length, localParmList.superiorGroup.value); + + if (isDryRun) { + localParmList.operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList.operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + CMS_DEBUG2(globalArea, traceLevel, + "Attemping to add group (%.*s).\n", + localParmList.group.length, localParmList.group.value); + + radminActionRC = executeAdminCommand(&radminStatus, operatorCommand, + serviceMessage, sizeof(serviceMessage), + authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, + "Failed to delete group (%.*s), rsn = %s\n", + localParmList.group.length, localParmList.group.value, + serviceMessage); + } + } + } + else if (localParmList.function == ZIS_GROUP_ADMIN_SRV_FC_DELETE) { +#define TSS_GRP_ADMIN_DEL_MAX_LEN 20 /* ACID - 8, Rest of command - 12 */ + char operatorCommand[TSS_GRP_ADMIN_DEL_MAX_LEN + 1] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), "TSS DELETE(%.*s)", + localParmList.group.length, localParmList.group.value); + + if (isDryRun) { + localParmList.operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList.operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + CMS_DEBUG2(globalArea, traceLevel, + "Attemping to delete group (%.*s).\n", + localParmList.group.length, localParmList.group.value); + + radminActionRC = executeAdminCommand(&radminStatus, operatorCommand, + serviceMessage, sizeof(serviceMessage), + authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, + "Failed to delete group (%.*s), rsn = %s\n", + localParmList.group.length, localParmList.group.value, + serviceMessage); + } + } + } + else if (localParmList.function == ZIS_GROUP_ADMIN_SRV_FC_CONNECT_USER) { +#define TSS_GRP_ADMIN_ADD_CON_MAX_LEN 37 /* ACID - 8, Group - 8, Rest of command - 21 */ + char operatorCommand[TSS_GRP_ADMIN_ADD_CON_MAX_LEN + 1] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), + "TSS ADDTO(%.*s) GROUP(%.*s)", + localParmList.user.length, localParmList.user.value, + localParmList.group.length, localParmList.group.value); + + + if (isDryRun) { + localParmList.operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList.operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + CMS_DEBUG2(globalArea, traceLevel, + "Attemping to add connection to (%.*s).\n", + localParmList.group.length, localParmList.group.value); + + radminActionRC = executeAdminCommand(&radminStatus, operatorCommand, + serviceMessage, sizeof(serviceMessage), + authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, + "Failed to add connection to (%.*s), rsn = %s\n", + localParmList.group.length, localParmList.group.value, + serviceMessage); + } + } + } + else if (localParmList.function == ZIS_GROUP_ADMIN_SRV_FC_REMOVE_USER) { +#define TSS_GRP_ADMIN_REM_CON_MAX_LEN 38 /* ACID - 8, Group - 8, Rest of command - 22 */ + char operatorCommand[TSS_GRP_ADMIN_REM_CON_MAX_LEN + 1] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), + "TSS REMOVE(%.*s) GROUP(%.*s)", + localParmList.user.length, localParmList.user.value, + localParmList.group.length, localParmList.group.value); + + + if (isDryRun) { + localParmList.operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList.operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + CMS_DEBUG2(globalArea, traceLevel, + "Attemping to remove connection to (%.*s).\n", + localParmList.group.length, localParmList.group.value); + + radminActionRC = executeAdminCommand(&radminStatus, operatorCommand, + serviceMessage, sizeof(serviceMessage), + authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, + "Failed to remove connection to (%.*s), rsn = %s\n", + localParmList.group.length, localParmList.group.value, + serviceMessage); + } + } + } + else { + return RC_ZIS_GRPASRV_BAD_FUNCTION; + } + + if (radminActionRC != RC_RADMIN_OK) { + localParmList.internalServiceRC = radminActionRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + + unsigned int copySize = strlen(serviceMessage); + if (copySize > sizeof(localParmList.serviceMessage.text)) { + copySize = sizeof(localParmList.serviceMessage.text); + } + + localParmList.serviceMessage.length = copySize; + memcpy(localParmList.serviceMessage.text, serviceMessage, copySize); + status = RC_ZIS_GRPASRV_INTERNAL_SERVICE_FAILED; + } + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGroupAdminServiceParmList)); + + return status; +} + +/* A small struct to hold the information + * returned from getGenresProfileOwnerHandler() + */ +typedef struct TSSOwnerData_t { + char owner[ZIS_USER_ID_MAX_LENGTH]; /* owner can also be a user id, so it has the same max length of 8 */ + TSSLogData *logData; +} TSSOwnerData; + +/* A handler function that returns the owner of a particular profile (resource). */ +static int getGenresProfileOwnerHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSOwnerData *parms = userData; + TSSLogData *logData = parms->logData; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + /* The output looks like: + * + * {OWNER} OWNS {CLASS} {PROFILE} + * + * I can easily get it by just reading in the first 8 characters + * of the line. + */ + if (currentLine->length >= sizeof(parms->owner)) { /* this should always be the case */ + memcpy(parms->owner, currentLine->text, sizeof(parms->owner)); + padWithSpaces(parms->owner, sizeof(parms->owner), 1, 1); + } + + return 0; +} + +/* A small struct to hold the information + * returned from isUserAlreadyPermitted() + */ +typedef struct PermittedData_t { + bool isPermitted; + const char *className; + const char *profileName; + TSSLogData *logData; +} PermittedData; + +/* A handler function that returns the whether or not a user is already permitted to a + * particular profile (resource) + */ +static int isUserAlreadyPermitted(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + PermittedData *parms = userData; + TSSLogData *logData = parms->logData; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + unsigned int offsetInBlock = 16; + + char searchKey[11 + 1] = {0}; + snprintf(searchKey, sizeof(searchKey), "XA %s", parms->className); + nullTerminate(searchKey, sizeof(searchKey) - 1); + + do { + while (offsetInBlock < currentBlock->lastByteOffset) { + if (tssIsOutputDone(currentLine->text, currentLine->length)) { + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + return -1; + } + else { + char tmpBuffer[ZIS_SECURITY_PROFILE_MAX_LENGTH] = {0}; + int keyIndex = tssFindDesiredKey(searchKey, keys); + if (keyIndex != -1) { + int ownerIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, TSS_OWNER_KEY); + if (ownerIndex == -1) { + return -1; + } + + int status = tssFindValueForKey(currentLine->text, ownerIndex-1, keys, + keyIndex, numberOfKeys, tmpBuffer, sizeof(tmpBuffer)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", searchKey); + return -1; + } + } + + int foundProfileLength = tssGetActualValueLength(tmpBuffer, sizeof(tmpBuffer)); + int searchProfileLength = strlen(parms->profileName); + + if (foundProfileLength == searchProfileLength) { + if (!memcmp(tmpBuffer, parms->profileName, searchProfileLength)) { + parms->isPermitted = TRUE; + return 0; + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Returns a string representation of an ENUM value for access type. */ +static const char *getAccessString(ZISGenresAdminServiceAccessType type) { + switch (type) { + case ZIS_GENRES_ADMIN_ACESS_TYPE_READ: + return "READ"; + case ZIS_GENRES_ADMIN_ACESS_TYPE_UPDATE: + return "UPDATE"; + case ZIS_GENRES_ADMIN_ACESS_TYPE_ALTER: + return "ALTER"; + case ZIS_GENRES_ADMIN_ACESS_TYPE_ALL: + return "ALL"; + default: + return NULL; + } +} + +/* A helper function to issue the command to revoke access to a resource. */ +static int handleRevokeAccess(ZISGenresAdminServiceParmList *localParmList, char *className, char *serviceMessage, int serviceMessageSize, + bool isDryRun, RadminStatus *radminStatus, RadminCallerAuthInfo authInfo) { +#define TSS_PRF_ACCESS_LIST_ADMIN_REVOKE_MAX_LEN 302 /* Class - 8, Profile - 255, Access - 7, Rest of command - 32 */ + char operatorCommand[TSS_PRF_ACCESS_LIST_ADMIN_REVOKE_MAX_LEN] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), "TSS REVOKE(%.*s) %s(%.*s)", + localParmList->user.length, localParmList->user.value, + className, localParmList->profile.length, + localParmList->profile.value); + + int radminRC = RC_RADMIN_OK; + + if (isDryRun) { + localParmList->operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList->operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + radminRC = executeAdminCommand(radminStatus, operatorCommand, + serviceMessage, serviceMessageSize, + authInfo); + } + + return radminRC; +} + +/* A helper function to issue a command to permit access to a resource. */ +static int handlePermitAccess(ZISGenresAdminServiceParmList *localParmList, char *className, char *serviceMessage, int serviceMessageSize, + bool isDryRun, RadminStatus *radminStatus, RadminCallerAuthInfo authInfo) { +#define TSS_PRF_ACCESS_LIST_ADMIN_PERMIT_MAX_LEN 302 /* Class - 8, Profile - 255, Access - 7, Rest of command - 32 */ + char operatorCommand[TSS_PRF_ACCESS_LIST_ADMIN_PERMIT_MAX_LEN] = {0}; + + if (localParmList->accessType == ZIS_GENRES_ADMIN_ACESS_TYPE_ALTER) { /* Top Secret doesn't have ALTER */ + localParmList->accessType = ZIS_GENRES_ADMIN_ACESS_TYPE_ALL; + } + + const char *accessString = getAccessString(localParmList->accessType); + + snprintf(operatorCommand, sizeof(operatorCommand), "TSS PERMIT(%.*s) %s(%.*s) ACCESS(%s)", + localParmList->user.length, localParmList->user.value, + className, localParmList->profile.length, + localParmList->profile.value, accessString); + + int radminRC = RC_RADMIN_OK; + + if (isDryRun) { + localParmList->operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList->operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + radminRC = executeAdminCommand(radminStatus, operatorCommand, + serviceMessage, serviceMessageSize, + authInfo); + } + + return radminRC; +} + +/* A helper function to issue a command to remove a resource */ +static int handleDeleteProfile(ZISGenresAdminServiceParmList *localParmList, char *className, char *ownerName, + char *serviceMessage, int serviceMessageSize, + bool isDryRun, RadminStatus *radminStatus, RadminCallerAuthInfo authInfo) { +#define TSS_PRF_ADMIN_DEL_MAX_LEN 286 /* Acid - 8, Class - 8, Profile - 255, Rest of command - 15 */ + char operatorCommand[TSS_PRF_ADMIN_DEL_MAX_LEN] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), "TSS REMOVE(%s) %s(%.*s)", + ownerName, className, localParmList->profile.length, + localParmList->profile.value); + + int radminRC = RC_RADMIN_OK; + + if (isDryRun) { + localParmList->operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList->operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + radminRC = executeAdminCommand(radminStatus, operatorCommand, + serviceMessage, serviceMessageSize, + authInfo); + } + + return radminRC; +} + +/* A helper function to issue a command to define a resource. */ +static int handleDefineProfile(ZISGenresAdminServiceParmList *localParmList, char *className, char *serviceMessage, int serviceMessageSize, + bool isDryRun, RadminStatus *radminStatus, RadminCallerAuthInfo authInfo) { +#define TSS_PRF_ADMIN_ADD_MAX_LEN 285 /* Acid - 8, Class - 8, Profile - 255, Rest of command - 14 */ + char operatorCommand[TSS_PRF_ADMIN_ADD_MAX_LEN] = {0}; + + snprintf(operatorCommand, sizeof(operatorCommand), "TSS ADDTO(%.*s) %s(%.*s)", + localParmList->owner.length, localParmList->owner.value, + className, localParmList->profile.length, + localParmList->profile.value); + + int radminRC = RC_RADMIN_OK; + + if (isDryRun) { + localParmList->operatorCommand.length = sizeof(operatorCommand); + memcpy(localParmList->operatorCommand.text, operatorCommand, + sizeof(operatorCommand)); + } + else { + radminRC = executeAdminCommand(radminStatus, operatorCommand, + serviceMessage, serviceMessageSize, + authInfo); + } + + return radminRC; +} + +/* Entry point into the genres profile admin endpoint logic */ +int zisGenresProfileAdminServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_GSADMNSRV_OK; + int radminActionRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_GSADMNSRV_PARMLIST_NULL; + } + + ZISGenresAdminServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGenresParmList(&localParmList); + if (parmValidateRC != RC_ZIS_GSADMNSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_GSADMNSRV_IMPERSONATION_MISSING; + } + + if (localParmList.owner.length == 0) { + memcpy(localParmList.owner.value, caller.value, sizeof(caller.value)); + localParmList.owner.length = caller.length; + } + + CMS_DEBUG2(globalArea, traceLevel, + "GSADMNSRV: flags = 0x%X, function = %d, " + "owner = \'%.*s\', user = \'%.*s\', access = %d'\n", + localParmList.flags, + localParmList.function, + localParmList.owner.length, localParmList.owner.value, + localParmList.user.length, localParmList.user.value, + localParmList.accessType); + CMS_DEBUG2(globalArea, traceLevel, "GSADMNSRV: profile = \'%.*s\'\n", + localParmList.profile.length, localParmList.profile.value); + CMS_DEBUG2(globalArea, traceLevel, "GSADMNSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + CrossMemoryServerConfigParm userClassParm = {0}; + int getParmRC = cmsGetConfigParm(&globalArea->serverName, + ZIS_PARMLIB_PARM_SECMGMT_USER_CLASS, + &userClassParm); + if (getParmRC != RC_CMS_OK) { + localParmList.internalServiceRC = getParmRC; + return RC_ZIS_GSADMNSRV_USER_CLASS_NOT_READ; + } + if (userClassParm.valueLength > ZIS_SECURITY_CLASS_MAX_LENGTH) { + return RC_ZIS_GSADMNSRV_USER_CLASS_TOO_LONG; + } + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + CMS_DEBUG2(globalArea, traceLevel, "GSADMNSRV: class = \'%.*s\'\n", + userClassParm.valueLength, userClassParm.charValueNullTerm); + + bool isDryRun = (localParmList.flags & ZIS_GROUP_ADMIN_FLAG_DRYRUN) ? + true : false; + + RadminStatus radminStatus = {0}; + + char serviceMessage[TSS_SERVICE_MSG_MAX_LEN + 1]; + + if (localParmList.function == ZIS_GENRES_ADMIN_SRV_FC_GIVE_ACCESS) { +#define TSS_PRF_PERMIT_EXTR_MAX_LEN 30 /* Acid - 8, Data - 11, Rest of command - 11 */ + char extractGenresProfilePermit[TSS_PRF_PERMIT_EXTR_MAX_LEN + 1] = {0}; + + snprintf(extractGenresProfilePermit, sizeof(extractGenresProfilePermit), + "TSS LIST(%.*s) DATA(XAUTH)", localParmList.user.length, + localParmList.user.value); + + char profileNameNullTerm[ZIS_SECURITY_PROFILE_MAX_LENGTH + 1] = {0}; + memcpy(profileNameNullTerm, localParmList.profile.value, + localParmList.profile.length); + + PermittedData parms = { + .isPermitted = FALSE, + .className = userClassParm.charValueNullTerm, + .profileName = profileNameNullTerm, + .logData = &logData + }; + + radminActionRC = radminRunRACFCommand( + authInfo, + extractGenresProfilePermit, + isUserAlreadyPermitted, + &parms, + &radminStatus + ); + + if (radminActionRC == RC_RADMIN_OK) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Attemping to permit access to %.*s for %.*s...\n", + localParmList.profile.length, localParmList.profile.value, + localParmList.user.length, localParmList.user.value); + + if (!parms.isPermitted || isDryRun) { + radminActionRC = handlePermitAccess(&localParmList, userClassParm.charValueNullTerm, serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + } + else { + radminActionRC = handleRevokeAccess(&localParmList, userClassParm.charValueNullTerm, serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + + if (radminActionRC == RC_RADMIN_OK) { + radminActionRC = handlePermitAccess(&localParmList, userClassParm.charValueNullTerm, serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + } + } + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Failed to permit access to (%.*s), rsn = %s\n", + localParmList.profile.length, localParmList.profile.value, + serviceMessage); + } + } + } + else if (localParmList.function == ZIS_GENRES_ADMIN_SRV_FC_REVOKE_ACCESS) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Attemping to revoke access to %.*s for %.*s...\n", + localParmList.profile.length, localParmList.profile.value, + localParmList.user.length, localParmList.user.value); + + radminActionRC = handleRevokeAccess(&localParmList, userClassParm.charValueNullTerm, serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Failed to revoke access to (%.*s), rsn = %s\n", + localParmList.profile.length, localParmList.profile.value, + serviceMessage); + } + } + else if (localParmList.function == ZIS_GENRES_ADMIN_SRV_FC_DEFINE) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Attemping to define profile %.*s to %s...\n", + localParmList.profile.length, localParmList.profile.value, + userClassParm.charValueNullTerm); + + radminActionRC = handleDefineProfile(&localParmList, userClassParm.charValueNullTerm, serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Failed to define profile %.*s to %s, rsn = %s\n", + localParmList.profile.length, localParmList.profile.value, + userClassParm.charValueNullTerm, serviceMessage); + } + } + else if (localParmList.function == ZIS_GENRES_ADMIN_SRV_FC_DELETE) { +#define TSS_PRF_OWNER_EXTR_MAX_LEN 274 /* Class - 8, Profile - 255, Rest of command - 11 */ + char extractGenresProfileOwner[TSS_PRF_OWNER_EXTR_MAX_LEN + 1] = {0}; + + snprintf(extractGenresProfileOwner, sizeof(extractGenresProfileOwner), + "TSS WHOO %s(%.*s)", userClassParm.charValueNullTerm, + localParmList.profile.length, localParmList.profile.value); + + TSSOwnerData parms = {0}; + + radminActionRC = radminRunRACFCommand( + authInfo, + extractGenresProfileOwner, + getGenresProfileOwnerHandler, + &parms, + &radminStatus + ); + + if (radminActionRC == RC_RADMIN_OK) { + char ownerNameNullTerm[ZIS_USER_ID_MAX_LENGTH + 1] = {0}; + memcpy(ownerNameNullTerm, parms.owner, sizeof(parms.owner)); + nullTerminate(ownerNameNullTerm, sizeof(ownerNameNullTerm) - 1); + + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Attemping to remove profile %.*s from %s...\n", + localParmList.profile.length, localParmList.profile.value, + userClassParm.charValueNullTerm); + + radminActionRC = handleDeleteProfile(&localParmList, userClassParm.charValueNullTerm, + ownerNameNullTerm, + serviceMessage, + sizeof(serviceMessage), isDryRun, + &radminStatus, authInfo); + + if (radminActionRC != RC_RADMIN_OK) { + CMS_DEBUG2(logData.globalArea, logData.traceLevel, + "Failed to remove profile %.*s from %s, rsn = %s\n", + localParmList.profile.length, localParmList.profile.value, + userClassParm.charValueNullTerm, serviceMessage); + } + } + } + else { + return RC_ZIS_GSADMNSRV_BAD_FUNCTION; + } + + if (radminActionRC != RC_RADMIN_OK) { + localParmList.internalServiceRC = radminActionRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + + unsigned copySize = strlen(serviceMessage); + if (copySize > sizeof(localParmList.serviceMessage.text)) { + copySize = sizeof(localParmList.serviceMessage.text); + } + + localParmList.serviceMessage.length = copySize; /* snprintf includes a '\0' */ + memcpy(localParmList.serviceMessage.text, serviceMessage, copySize); + status = RC_ZIS_GSADMNSRV_INTERNAL_SERVICE_FAILED; + } + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGenresAdminServiceParmList)); + + CMS_DEBUG2(globalArea, traceLevel, + "GSADMNSRV: status = %d, msgLen = %zu, cmdLen = %zu, " + "RC = %d, internal RC = %d, RSN = %d, " + "SAF RC = %d, RACF RC = %d, RSN = %d\n", + status, + localParmList.serviceMessage.length, + localParmList.operatorCommand.length, + radminActionRC, + localParmList.internalServiceRC, + localParmList.internalServiceRSN, + localParmList.safStatus.safRC, + localParmList.safStatus.racfRC, + localParmList.safStatus.racfRSN); + + return status; +} + +/* A struct to hold information about where + * in the output we left off. + */ +typedef struct TSSLineData_t { + const RadminVLText *linePtr; + const RadminCommandOutput *blockPtr; + unsigned int offsetInBlock; +} TSSLineData; + +/* A parameter list passed into the handler function + * for the genres profile extraction service. + */ +typedef struct TSSGenresProfileParms_t { + ZISGenresProfileEntry *tmpResultBuffer; + unsigned int profilesExtracted; + unsigned int profilesToExtract; + const char *className; + TSSLineData *lineData; + int startIndex; + bool reachedEndOfOutput; + TSSLogData *logData; +} TSSGenresProfileParms; + +/* Iterates through the tmpResultBuffer (ZISGenresProfileEntry array) to check for a duplicate + * profile. This is a slow solution, but based on the structure of the output, there isn't really + * a more reliable way to ensure that the same profile isn't added twice. + */ +static int isDuplicateProfile(ZISGenresProfileEntry *buffer, char *searchString, int searchStringBufferSize, + int stopSearch) { + for (int i = 0; i < stopSearch; i++) { + int searchStringLength = tssGetActualValueLength(searchString, searchStringBufferSize); + int bufferStringLength = tssGetActualValueLength(buffer[i].profile, sizeof(buffer[i].profile)); + + if (searchStringLength == bufferStringLength) { + if (!memcmp(searchString, buffer[i].profile, searchStringLength)) { + return TRUE; + } + } + } + + return FALSE; +} + +/* A helper function that calls an r_admin command to query the database + * for a profile owner. + */ +static int assignCorrectProfileOwners(ZISGenresProfileEntry *buffer, unsigned int numberOfProfiles, + const char *className, RadminStatus *radminStatus, + RadminCallerAuthInfo authInfo) { + for (int i = 0; i < numberOfProfiles; i++) { + if (buffer[i].owner[0] == 0) { +#define TSS_PRF_OWNER_EXTR_MAX_LEN 274 /* Class - 8, Profile - 255, Rest of command - 11 */ + char extractGenresProfileOwner[TSS_PRF_OWNER_EXTR_MAX_LEN + 1] = {0}; + + snprintf(extractGenresProfileOwner, sizeof(extractGenresProfileOwner), + "TSS WHOO %s(%.*s)", className, + sizeof(buffer[i].profile), buffer[i].profile); + + TSSOwnerData parms = {0}; + + int radminRC = radminRunRACFCommand( + authInfo, + extractGenresProfileOwner, + getGenresProfileOwnerHandler, + &parms, + radminStatus + ); + + if (radminRC != RC_RADMIN_OK) { + return radminRC; + } + + ZISGenresProfileEntry *p = &buffer[i]; + memcpy(p->owner, parms.owner, sizeof(p->owner)); + } + } + + return 0; +} + +/* Shifts the data extracted to the left (index of choice now starts at index 0) */ +static int shiftProfileBufferLeft(ZISGenresProfileEntry *buffer, unsigned int numberOfProfiles, + int foundAt) { + + unsigned int copyIndex = foundAt; + for (int i = 0; i < numberOfProfiles; i++) { + memset(buffer[i].profile, 0, sizeof(buffer[i].profile)); + memset(buffer[i].owner, 0, sizeof(buffer[i].owner)); + + memcpy(buffer[i].profile, buffer[copyIndex].profile, sizeof(buffer[copyIndex].profile)); + memcpy(buffer[i].owner, buffer[copyIndex].owner, sizeof(buffer[copyIndex].owner)); + + memset(buffer[copyIndex].profile, 0, sizeof(buffer[copyIndex].profile)); + memset(buffer[copyIndex].owner, 0, sizeof(buffer[copyIndex].owner)); + + copyIndex++; + } +} + +/* Searches the extracted profiles to check for a match. The index where the match occurs + * is returned via foundAt. + */ +static bool findStartProfile(ZISGenresProfileEntry *buffer, unsigned int numberOfProfiles, + const char *searchProfile, int *foundAt) { + int searchProfileLength = strlen(searchProfile); + + for (int i = 0; i < numberOfProfiles; i++) { + int extractedProfileLength = tssGetActualValueLength(buffer[i].profile, + sizeof(buffer[i].profile)); + + if (searchProfileLength == extractedProfileLength) { + if (!memcmp(searchProfile, buffer[i].profile, searchProfileLength)) { + *foundAt = i; + return TRUE; + } + } + } + + return FALSE; +} + +/* A handler function that is called after the r_admin command is issued. It parses the profile + * data from the output by going line by line. + */ +static int genresProfileHandler(RadminAPIStatus status, const RadminCommandOutput *result, + void *userData) { + if (status.racfRC != 0) { + return status.racfRC; + } + + TSSGenresProfileParms *parms = userData; + ZISGenresProfileEntry *buffer = parms->tmpResultBuffer; + TSSLogData *logData = parms->logData; + ZISGenresProfileEntry entry = {0}; + + const RadminCommandOutput *currentBlock = result; + + if (currentBlock == NULL) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "No output was found in output message entry on first block...\n"); + return 0; + } + + const RadminVLText *currentLine = ¤tBlock->firstMessageEntry; + + /* Look for an obvious error before we start parsing. + * The output will look something like: + * + * TSS{SOME NUMBERS} {SOME MESSAGE} + * TSS0301I TSS FUNCTION FAILED, RETURN CODE = {SOME NUMBER} + */ + bool errorDetected = tssIsErrorDetected(currentLine->text, currentLine->length); + if (errorDetected) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Error detected!\n"); + return -1; + } + + unsigned int offsetInBlock = 16; + + if (parms->lineData->blockPtr != NULL && parms->lineData->linePtr != NULL && parms->lineData->offsetInBlock != 0) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, "Continuing...\n"); + currentBlock = parms->lineData->blockPtr; + currentLine = parms->lineData->linePtr; + offsetInBlock = parms->lineData->offsetInBlock; + } + + /* Iterate through the output and store information on the profiles that + * are found. + */ + unsigned int numExtracted = parms->startIndex; + do { + char currOwner[ZIS_USER_ID_MAX_LENGTH] = {0}; + while (offsetInBlock < currentBlock->lastByteOffset) { + if (tssIsOutputDone(currentLine->text, currentLine->length)) { + parms->profilesExtracted = numExtracted; + parms->reachedEndOfOutput = TRUE; + + return 0; + } + + if (numExtracted == parms->profilesToExtract) { + parms->profilesExtracted = numExtracted; + parms->lineData->blockPtr = currentBlock; + parms->lineData->linePtr = currentLine; + parms->lineData->offsetInBlock = offsetInBlock; + + return 0; + } + + TSSKeyData keys[TSS_MAX_NUM_OF_KEYS] = {0}; + + int numberOfKeys = tssParseLineForKeys(currentLine->text, currentLine->length, keys); + if (numberOfKeys == -1 || numberOfKeys == 0) { + /* In this case, print the line in question. */ + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "An invalid number of keys were detected on line: '" + "%.*s'\n", currentLine->length, currentLine->text); + return -1; + } + else { + int keyIndex = tssFindDesiredKey(parms->className, keys); + if (keyIndex != -1) { + int ownerIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, TSS_OWNER_KEY); + if (ownerIndex == -1) { + return -1; + } + + int status = tssFindValueForKey(currentLine->text, ownerIndex-1, keys, + keyIndex, numberOfKeys, entry.profile, + sizeof(entry.profile)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", parms->className); + return -1; + } + + memcpy(currOwner, currentLine->text+ownerIndex+6, sizeof(currOwner)); + padWithSpaces(currOwner, sizeof(currOwner), 1, 1); + + if (!isDuplicateProfile(buffer, entry.profile, sizeof(entry.profile), numExtracted)) { + ZISGenresProfileEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + + memcpy(p->owner, currOwner, sizeof(currOwner)); + memcpy(p->profile, entry.profile, sizeof(p->profile)); + + memset(&entry, 0, sizeof(ZISGenresProfileEntry)); + } + } + else { + int keyIndex = tssFindDesiredKey(TSS_XAUTH_KEY, keys); + if (keyIndex != -1) { + int acidIndex = lastIndexOfString((char*)currentLine->text, currentLine->length, TSS_ACID_KEY); + if (acidIndex == -1) { + return -1; + } + + int status = tssFindValueForKey(currentLine->text, acidIndex-1, keys, + keyIndex, numberOfKeys, entry.profile, + sizeof(entry.profile)); + if (status == -1) { + CMS_DEBUG2(logData->globalArea, logData->traceLevel, + "Could not find value of %s...\n", TSS_XAUTH_KEY); + return -1; + } + + if (!isDuplicateProfile(buffer, entry.profile, sizeof(entry.profile), numExtracted)) { + ZISGenresProfileEntry *p = &buffer[numExtracted++]; + memset(p, 0, sizeof(*p)); + + memcpy(p->owner, currOwner, sizeof(currOwner)); /* They will have the same owner as the profile prefix. */ + memcpy(p->profile, entry.profile, sizeof(p->profile)); + + memset(&entry, 0, sizeof(ZISGenresProfileEntry)); + } + } + } + } + + offsetInBlock += currentLine->length + 2; + currentLine = (RadminVLText *)(currentLine->text + currentLine->length); + } + + currentBlock = currentBlock->next; + if (currentBlock != NULL) { + currentLine = (RadminVLText *)&(currentBlock->firstMessageEntry); + offsetInBlock = 16; + } + } while (currentBlock != NULL); + + return 0; +} + +/* Prepares to copy the ZISGenresProfileEntry struct to the other address space where + * it can be returned to the caller. + */ +static void copyGenresProfiles(ZISGenresProfileEntry *dest, + const ZISGenresProfileEntry *src, + unsigned int count, + int startIndex) { + for (unsigned int i = 0; i < count; i++) { + cmCopyToSecondaryWithCallerKey(dest[i].profile, src[startIndex].profile, + sizeof(dest[i].profile)); + cmCopyToSecondaryWithCallerKey(dest[i].owner, src[startIndex].owner, + sizeof(dest[i].owner)); + startIndex++; + } +} + +/* The entry point into the genres profiles endpoint logic. Note, a general resource + * followed by a permit to that resource in Top Secret is the equivalent of a profile + * in RACF. + */ +int zisGenresProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm) { + int status = RC_ZIS_GRPRFSRV_OK; + int radminExtractRC = RC_RADMIN_OK; + + void *clientParmAddr = parm; + if (clientParmAddr == NULL) { + return RC_ZIS_GRPRFSRV_PARMLIST_NULL; + } + + ZISGenresProfileServiceParmList localParmList; + cmCopyFromSecondaryWithCallerKey(&localParmList, clientParmAddr, + sizeof(localParmList)); + + int parmValidateRC = validateGenresProfileParmList(&localParmList); + if (parmValidateRC != RC_ZIS_GRPRFSRV_OK) { + return parmValidateRC; + } + + int traceLevel = localParmList.traceLevel; + if (globalArea->pcLogLevel > traceLevel) { + traceLevel = globalArea->pcLogLevel; + } + + RadminUserID caller; + if (!secmgmtGetCallerUserID(&caller)) { + return RC_ZIS_GRPRFSRV_IMPERSONATION_MISSING; + } + + CMS_DEBUG2(globalArea, traceLevel, + "GRPRFSRV: profile = \'%.*s\'\n", + localParmList.startProfile.length, + localParmList.startProfile.value); + CMS_DEBUG2(globalArea, traceLevel, + "GRPRFSRV: class = \'%.*s\', profilesToExtract = %u," + " result buffer = 0x%p\n", + localParmList.class.length, localParmList.class.value, + localParmList.profilesToExtract, + localParmList.resultBuffer); + CMS_DEBUG2(globalArea, traceLevel, "GRPRFSRV: caller = \'%.*s\'\n", + caller.length, caller.value); + + RadminCallerAuthInfo authInfo = { + .acee = NULL, + .userID = caller + }; + + char classNullTerm[ZIS_SECURITY_CLASS_MAX_LENGTH + 1] = {0}; + memcpy(classNullTerm, localParmList.class.value, + localParmList.class.length); + char profileNameBuffer[ZIS_SECURITY_PROFILE_MAX_LENGTH + 1] = {0}; + memcpy(profileNameBuffer, localParmList.startProfile.value, + localParmList.startProfile.length); + const char *startProfileNullTerm = localParmList.startProfile.length > 0 ? + profileNameBuffer : NULL; + + if (localParmList.class.length == 0) { + CrossMemoryServerConfigParm userClassParm = {0}; + int getParmRC = cmsGetConfigParm(&globalArea->serverName, + ZIS_PARMLIB_PARM_SECMGMT_USER_CLASS, + &userClassParm); + if (getParmRC != RC_CMS_OK) { + localParmList.internalServiceRC = getParmRC; + return RC_ZIS_GRPRFSRV_USER_CLASS_NOT_READ; + } + if (userClassParm.valueLength > ZIS_SECURITY_CLASS_MAX_LENGTH) { + return RC_ZIS_GRPRFSRV_CLASS_TOO_LONG; + } + memcpy(classNullTerm, userClassParm.charValueNullTerm, + userClassParm.valueLength); + } + + size_t tmpResultBufferSize = + sizeof(ZISGenresProfileEntry) * localParmList.profilesToExtract; + + int allocRC = 0, allocSysRC = 0, allocSysRSN = 0; + + ZISGenresProfileEntry *tmpResultBuffer = + (ZISGenresProfileEntry *)safeMalloc64v3( + tmpResultBufferSize, TRUE, + "ZISGenresProfileEntry", + &allocRC, + &allocSysRSN, + &allocSysRSN + ); + + CMS_DEBUG2(globalArea, traceLevel, + "GRPRFSRV: tmpBuff allocated @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + allocRC, allocSysRC, allocSysRSN); + + if (tmpResultBuffer == NULL) { + return RC_ZIS_GRPRFSRV_ALLOC_FAILED; + } + + RadminStatus radminStatus = {0}; + + TSSLogData logData = { + .globalArea = globalArea, + .traceLevel = traceLevel + }; + + TSSLineData lineData = {0}; + + TSSGenresProfileParms parms = { + .tmpResultBuffer = tmpResultBuffer, + .profilesToExtract = localParmList.profilesToExtract, + .className = classNullTerm, + .lineData = &lineData, + .logData = &logData + }; + + + bool isProfileFound = FALSE; + int foundAt = -1; + do { +#define TSS_EXTRACT_PROFILE_CMD_MAX_LEN 33 /* Class - 8, Data - 10, Rest of command - 15 */ + char extractGenresProfileCommand[TSS_EXTRACT_PROFILE_CMD_MAX_LEN + 1] = {0}; + snprintf(extractGenresProfileCommand, sizeof(extractGenresProfileCommand), "TSS WHOH %s(*) DATA(MASK)", classNullTerm); + + radminExtractRC = radminRunRACFCommand( + authInfo, + extractGenresProfileCommand, + genresProfileHandler, + &parms, + &radminStatus + ); + + if (startProfileNullTerm == NULL) { + CMS_DEBUG2(globalArea, traceLevel, "No start profile was specified...\n"); + break; /* We aren't starting from a specific profile, so just return everything. */ + } + + if (!isProfileFound) { + isProfileFound = findStartProfile(tmpResultBuffer, parms.profilesExtracted, startProfileNullTerm, &foundAt); + if (!isProfileFound) { + CMS_DEBUG2(globalArea, traceLevel, "Profile was not found on current interation, clearing buffer...\n"); + memset(tmpResultBuffer, 0, tmpResultBufferSize); /* We found nothing of value, just clear the array. */ + } + + if (foundAt == 0) { + CMS_DEBUG2(globalArea, traceLevel, "Profile was found at index #0, nothing needs to be done...\n"); + break; /* Nothing needs to be done. Just return the output. */ + } + } + + if (isProfileFound) { + CMS_DEBUG2(globalArea, traceLevel, "Profile was found at index #%d, shifting everything left...\n", foundAt); + + unsigned int validExtractedProfiles = parms.profilesExtracted - foundAt; + CMS_DEBUG2(globalArea, traceLevel, "The valid number of extracted profiles is %d...\n", validExtractedProfiles); + + if (foundAt != 0) { + shiftProfileBufferLeft(tmpResultBuffer, validExtractedProfiles, foundAt); + foundAt = 0; /* it will now always be index 0 */ + } + + if (parms.reachedEndOfOutput) { + CMS_DEBUG2(globalArea, traceLevel, "The end of the output has been reached...\n"); + parms.profilesExtracted = validExtractedProfiles; + break; /* Just return the output. Everything has already been shifted. */ + } + + if (validExtractedProfiles >= localParmList.profilesToExtract) { + CMS_DEBUG2(globalArea, traceLevel, "There are enough profiles left...\n"); + parms.profilesExtracted = validExtractedProfiles; + break; /* Just return the output. Everything up to the requested count will be returned */ + } + + if (validExtractedProfiles < localParmList.profilesToExtract) { + CMS_DEBUG2(globalArea, traceLevel, "Not enough profiles, looping again...\n"); + parms.startIndex = validExtractedProfiles; /* Start at the index after the last valid profile. */ + } + } + + if (parms.reachedEndOfOutput) { + CMS_DEBUG2(globalArea, traceLevel, "The start profile was not found...\n"); + parms.profilesExtracted = 0; /* We didn't find the profile, so we should return an error. */ + break; + } + + } while (1); + + CMS_DEBUG2(globalArea, traceLevel, + "Extracted %d genres profiles\n", + parms.profilesExtracted); + + /* Return some sort of error to prevent + * having an empty array. Note, even if having no profiles was correct, + * returning an empty array is not. + */ + if (parms.profilesExtracted == 0 && radminExtractRC == RC_RADMIN_OK) { + CMS_DEBUG2(globalArea, traceLevel, "No genres profiles could be found...\n"); + radminExtractRC = RC_RADMIN_NONZERO_USER_RC; + } + + if (radminExtractRC == RC_RADMIN_OK) { + if (startProfileNullTerm) { + CMS_DEBUG2(globalArea, traceLevel, "Assigning correct profile owners...\n"); + radminExtractRC = assignCorrectProfileOwners(tmpResultBuffer, parms.profilesExtracted, classNullTerm, + &radminStatus, authInfo); + } + } + + if (radminExtractRC == RC_RADMIN_OK) { + copyGenresProfiles(localParmList.resultBuffer, tmpResultBuffer, + parms.profilesExtracted, 0); + localParmList.profilesExtracted = parms.profilesExtracted; + } else { + localParmList.internalServiceRC = radminExtractRC; + localParmList.internalServiceRSN = radminStatus.reasonCode; + localParmList.safStatus.safRC = radminStatus.apiStatus.safRC; + localParmList.safStatus.racfRC = radminStatus.apiStatus.racfRC; + localParmList.safStatus.racfRSN = radminStatus.apiStatus.racfRSN; + status = RC_ZIS_GRPRFSRV_INTERNAL_SERVICE_FAILED; + } + + int freeRC = 0, freeSysRC = 0, freeSysRSN = 0; + + freeRC = safeFree64v3(tmpResultBuffer, tmpResultBufferSize, + &freeSysRC, &freeSysRSN); + + CMS_DEBUG2(globalArea, traceLevel, "GRPRFSRV: tmpBuff free'd @ 0x%p (%zu %d %d %d)\n", + tmpResultBuffer, tmpResultBufferSize, + freeRC, freeSysRC, freeSysRSN); + + tmpResultBuffer = NULL; + + cmCopyToSecondaryWithCallerKey(clientParmAddr, &localParmList, + sizeof(ZISGenresProfileServiceParmList)); + + CMS_DEBUG2(globalArea, traceLevel, + "GRPRFSRV: status = %d, extracted = %d, RC = %d, " + "internal RC = %d, RSN = %d, " + "SAF RC = %d, RACF RC = %d, RSN = %d\n", + status, + localParmList.profilesExtracted, radminExtractRC, + localParmList.internalServiceRC, + localParmList.internalServiceRSN, + localParmList.safStatus.safRC, + localParmList.safStatus.racfRC, + localParmList.safStatus.racfRSN); + + return status; +} + +/* + 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/zis/services/tssparsing.c b/c/zis/services/tssparsing.c new file mode 100644 index 000000000..17a19f34d --- /dev/null +++ b/c/zis/services/tssparsing.c @@ -0,0 +1,395 @@ + + +/* + 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 + +#ifndef METTLE +#error Non-metal C code is not supported +#endif + +#include "zowetypes.h" +#include "utils.h" + +#include "zis/services/tssparsing.h" + +/* If an error has occured, it will be the first thing printed + * in the output. We should use this information to prescreen for + * any errors before any more parsing happens. + */ +bool tssIsErrorDetected(const char *str, unsigned int len) { + if (len < TSS_MESSAGE_ID_MAX_LEN) { + return FALSE; + } + + if (!memcmp(str, TSS_FUNCTION_END, strlen(TSS_FUNCTION_END))) { + if (memcmp(str, TSS_COMMAND_SUCCESS, strlen(TSS_COMMAND_SUCCESS))) { + return TRUE; + } + } + + return FALSE; +} + +/* If a line is reached that starts with the prefix "TSS", + * then we have reached the end of the output. Followed by + * "TSS" is the reason and return codes. + */ +bool tssIsOutputDone(const char *str, unsigned int len) { + if (len < strlen(TSS_FUNCTION_END)) { + return FALSE; + } + + if (!memcmp(str, TSS_FUNCTION_END, strlen(TSS_FUNCTION_END))) { + return TRUE; + } + + return FALSE; +} + +/* Searches the keys found for a match. This determines whether + * the key you are looking for is present on the current line of + * output. + */ +int tssFindDesiredKey(const char *searchStr, TSSKeyData *keys) { + int searchLen = strlen(searchStr); + + for (int i = 0; i < 2; i++) { + if (keys[i].keyLength == searchLen) { + if (!memcmp(keys[i].keyName, searchStr, searchLen)) { + return i; + } + } + } + + return -1; +} + +/* A helper function that fills in the names of the keys + * based on the determined offsets on the output. + */ +static int tssFindNameForKeys(const char *str, TSSKeyData *keys) { + for (int i = 0; i < 2; i++) { + memcpy(keys[i].keyName, str+keys[i].startPos, keys[i].keyLength); + } +} + +/* All of the values found are space padded to be consistent + * with the rest of the API. This function is used to find the + * length without the padding. + */ +int tssGetActualValueLength(const char *str, unsigned int len) { + for (int i = len - 1; i >= 0; i--) { + if (str[i] != ' ' && str[i] != '\0') { + return i + 1; + } + } + + return -1; +} + +/* In the output, the length of the line is not zero even + * if there is no relevant data. You must check for a line + * full of spaces. + */ +bool tssIsLineBlank(const char *str, unsigned int len) { + for (int i = 0; i < len; i++) { + if (str[i] != ' ') { + return FALSE; + } + } + return TRUE; +} + +/* Calls a series of helper functions that parse the + * current line of output and fill in the keys in the + * TSSKeyData struct array. + */ +int tssParseLineForKeys(const char *str, unsigned int len, TSSKeyData *keys) { + if (tssIsLineBlank(str, len)) { + return 0; + } + + int numberOfKeys = tssFindNumberOfKeys(str, len, 0); + if (numberOfKeys > 2 || numberOfKeys == 0) { + return -1; + } + + int status = tssFindAllPossibleKeys(str, len, keys, numberOfKeys); + if (status == -1) { + return -1; + } + + tssFindNameForKeys(str, keys); + + return numberOfKeys; +} + +/* A helper function that parses the current line of output to see if there are + * any keys based on our criteria. + */ +static int tssFindNumberOfKeys(const char *str, int unsigned len, int startPos) { + int searchLen = strlen(TSS_KEY_DELIMITER); + int lastPossibleStart = len-searchLen; + int pos = startPos; + int keyCount = 0; + + if (startPos > lastPossibleStart){ + return 0; + } + + while (pos <= lastPossibleStart){ + if (!memcmp(str+pos,TSS_KEY_DELIMITER,searchLen)){ + keyCount++; + } + pos++; + } + + return keyCount; +} + +/* A helper function that finds the start of the key by going + * character by character from the delimiter. If we find two spaces in + * a row, we denote that as the start of the key. + */ +static int tssFindStartOfKeyReverse(const char *str, unsigned int len, int startPos) { + int currPos = startPos; + while (currPos > 1) { + if (str[currPos-1] == ' ' && str[currPos-2] == ' ') { + return currPos; + } + currPos--; + } + + return -1; +} + +/* A helper function that finds the end of the key by looking + * for the first non space from the delimiter. + */ +static int tssFindEndOfKeyReverse(const char *str, unsigned int len, int startPos) { + int currPos = startPos; + while (currPos >= 0) { + if (str[currPos] != ' ') { + return currPos; + } + currPos--; + } + + return -1; +} + +/* A helper function that returns the other index of the array + * where the key is stored. + */ +static int tssGetOtherKeyIndex(int keyIndex) { + if (keyIndex == 0) { + return 1; + } + else { + return 0; + } +} + +/* A helper function that finds the value of a key if there is only one key + * on the current line of output. It copies the value into a buffer and pads + * it with spaces. + */ +static int tssFindValueSingleKey(const char *str, unsigned int len, TSSKeyData *keys, int keyIndex, + char *valueBuffer, unsigned int valueBufferSize) { + int valStartPos = keys[keyIndex].delimPos + TSS_KEY_DELIMITER_LEN; + int valEndPos = -1; + + if (valStartPos + valueBufferSize >= len - 1) { + valEndPos = len - 1; + } + else { + valEndPos = valStartPos + valueBufferSize - 1; + } + + int valLen = valEndPos - valStartPos + 1; + if (valLen > valueBufferSize) { + return -1; + } + + memcpy(valueBuffer, str+valStartPos, valLen); + + padWithSpaces(valueBuffer, valueBufferSize, 1, 1); + + return 0; +} + +/* A helper function that finds the value of a key if there are two keys + * on the current line of output. It copies the value into a buffer and pads + * it with spaces. This function is needed in order to not go too far and + * into the other key value pair. + */ +static int tssFindValueDoubleKey(const char *str, unsigned int len, TSSKeyData *keys, int keyIndex, + char *valueBuffer, unsigned int valueBufferSize) { + int otherKeyIndex = -1; + otherKeyIndex = tssGetOtherKeyIndex(keyIndex); + + int valStartPos = keys[keyIndex].delimPos + TSS_KEY_DELIMITER_LEN; + int valEndPos = -1; + + if (valStartPos + valueBufferSize >= len - 1) { + valEndPos = len - 1; + } + else { + valEndPos = valStartPos + valueBufferSize - 1; + } + + if (valEndPos >= keys[otherKeyIndex].startPos && valEndPos <= keys[otherKeyIndex].endPos) { + valEndPos = keys[otherKeyIndex].startPos - 3; + } + + int valLen = valEndPos - valStartPos + 1; + if (valLen > valueBufferSize) { + return -1; + } + + memcpy(valueBuffer, str+valStartPos, valLen); + + padWithSpaces(valueBuffer, valueBufferSize, 1, 1); + + return 0; +} + +/* Calls a series of helper functions that find the value of a key + * and return it in a buffer to the caller. + */ +int tssFindValueForKey(const char *str, unsigned int len, TSSKeyData *keys, int keyIndex, + int numberOfKeys, char *valueBuffer, unsigned int valueBufferSize) { + int status = -1; + switch(numberOfKeys) { + case 1: status = tssFindValueSingleKey(str, len, keys, keyIndex, valueBuffer, + valueBufferSize); + return status; + case 2: status = tssFindValueDoubleKey(str, len, keys, keyIndex, valueBuffer, + valueBufferSize); + return status; + default: return status; + } +} + +/* A helper function that finds the offsets of the key and fills + * it into the TSSKeyData struct. This function deals with one key + * on the line of output. + */ +static int tssFindSingleKey(const char *str, unsigned int len, TSSKeyData *keys) { + if (str[0] != ' ') { + keys[0].startPos = 0; + } + else { + for (int i = 0; i < len - 1; i++) { + if (str[i] != ' ') { + keys[0].startPos = i; + break; + } + } + + if (keys[0].startPos == -1) { + return -1; + } + } + + int firstKeyDelimPos = indexOfString((char *)str, len, TSS_KEY_DELIMITER, 0); + if (firstKeyDelimPos == -1) { + return -1; + } + + keys[0].delimPos = firstKeyDelimPos; + + int firstKeyEndPos = tssFindEndOfKeyReverse(str, len, firstKeyDelimPos); + if (firstKeyEndPos == -1) { + return -1; + } + + keys[0].endPos = firstKeyEndPos; + + int keyLen = keys[0].endPos - keys[0].startPos + 1; + if (keyLen > TSS_MAX_KEY_LENGTH) { + return -1; + } + + keys[0].keyLength = keyLen; + + return 0; +} + +/* A helper function that finds the offsets of the key and fills + * it into the TSSKeyData struct. This function deals with two keys + * on the same line of output. + */ +static int tssFindDoubleKey(const char *str, unsigned int len, TSSKeyData *keys) { + int status = tssFindSingleKey(str, len, keys); + if (status == -1) { + return -1; + } + + int secondKeyDelimPos = len - strlen(TSS_KEY_DELIMITER); + secondKeyDelimPos = lastIndexOfString((char *)str, len, TSS_KEY_DELIMITER); + if (secondKeyDelimPos == -1) { + return -1; + } + + keys[1].delimPos = secondKeyDelimPos; + + int secondKeyEndPos = tssFindEndOfKeyReverse(str, len, secondKeyDelimPos); + if (secondKeyEndPos == -1) { + return -1; + } + + keys[1].endPos = secondKeyEndPos; + + int secondKeyStartPos = tssFindStartOfKeyReverse(str, len, secondKeyEndPos); + if (secondKeyStartPos == -1) { + return -1; + } + + keys[1].startPos = secondKeyStartPos; + + int keyLen = keys[1].endPos - keys[1].startPos + 1; + if (keyLen > TSS_MAX_KEY_LENGTH) { + return -1; + } + + keys[1].keyLength = keyLen; + + return 0; +} + +/* A helper function that finds the offsets of the key based on + * the number of keys found in the current line of output. + */ +static int tssFindAllPossibleKeys(const char *str, unsigned int len, TSSKeyData *keys, int numberOfKeys) { + int status = -1; + switch(numberOfKeys) { + case 1: status = tssFindSingleKey(str, len, keys); + return status; + case 2: status = tssFindDoubleKey(str, len, keys); + return status; + default: return status; + } +} + +/* + 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 b9d1685e1..57615c9b1 100644 --- a/c/zss.c +++ b/c/zss.c @@ -67,6 +67,11 @@ #include "zssLogging.h" #include "serviceUtils.h" +#include "unixFileService.h" +#include "omvsService.h" +#include "datasetService.h" + +#include "jwt.h" #define PRODUCT "ZLUX" #ifndef PRODUCT_MAJOR_VERSION @@ -91,9 +96,10 @@ static int traceLevel = 0; static int stringEndsWith(char *s, char *suffix); static void dumpJson(Json *json); static JsonObject *readPluginDefinition(ShortLivedHeap *slh, char *pluginIdentifier, char *pluginLocation); -static WebPluginListElt* readWebPluginDefinitions(HttpServer* server, ShortLivedHeap *slh, char *dirname); +static WebPluginListElt* readWebPluginDefinitions(HttpServer* server, ShortLivedHeap *slh, char *dirname, + const char *serverConfigFile); static JsonObject *readServerSettings(ShortLivedHeap *slh, const char *filename); -static InternalAPIMap *makeInternalAPIMap(); +static InternalAPIMap *makeInternalAPIMap(void); static int servePluginDefinitions(HttpService *service, HttpResponse *response){ zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "begin %s\n", __FUNCTION__); @@ -216,11 +222,6 @@ int serveLoginWithSessionToken(HttpService *service, HttpResponse *response) { addStringHeader(response,"Transfer-Encoding","chunked"); addStringHeader(response, "Cache-control", "no-store"); addStringHeader(response, "Pragma", "no-cache"); - /* HACK: the cookie header should be set inside serviceAuthNativeWithSessionToken, */ - /* but that is currently broken: the header doesn't get sent if it is set there */ - if (response->sessionCookie){ - addStringHeader(response,"Set-Cookie",response->sessionCookie); - } writeHeader(response); jsonStart(p); @@ -409,7 +410,7 @@ static MapItem mapItems[] ={ {0,0} }; -static InternalAPIMap *makeInternalAPIMap() { +static InternalAPIMap *makeInternalAPIMap(void) { /* allocate a single chunk for the struct and all the pointers. */ const int entryCount = ((sizeof mapItems)/(sizeof(mapItems[0])))-1; const int pointerArraySize = entryCount*(sizeof (void*)); @@ -561,7 +562,37 @@ static void installWebPluginDefintionsService(WebPluginListElt *webPlugins, Http zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "end %s\n", __FUNCTION__); } -static WebPluginListElt* readWebPluginDefinitions(HttpServer *server, ShortLivedHeap *slh, char *dirname) { +static int checkLoggingVerbosity(const char *serverConfigFile, char *pluginIdentifier, ShortLivedHeap *slh) { + char errorBuffer[1024] = {0}; + + Json *json = jsonParseFile(slh, serverConfigFile, errorBuffer, sizeof(errorBuffer)); + if (json) { + if (jsonIsObject(json)) { + JsonObject *jsonObject = jsonAsObject(json); + JsonObject *logLevels = jsonObjectGetObject(jsonObject, "logLevels"); + if (logLevels != NULL) { + JsonProperty *property = jsonObjectGetFirstProperty(logLevels); + while (property != NULL) { + if (!strcmp(jsonPropertyGetKey(property), pluginIdentifier)) { + int logLevel = jsonObjectGetNumber(logLevels, pluginIdentifier); + if (logLevel <= ZOWE_LOG_DEBUG3 || logLevel >= ZOWE_LOG_ALWAYS) { + return logLevel; + } + else { + break; + } + } + property = jsonObjectGetNextProperty(property); + } + } + } + } + + return ZOWE_LOG_INFO; +} + +static WebPluginListElt* readWebPluginDefinitions(HttpServer *server, ShortLivedHeap *slh, char *dirname, + const char *serverConfigFile) { int pluginDefinitionCount = 0; int returnCode; int reasonCode; @@ -577,6 +608,7 @@ static WebPluginListElt* readWebPluginDefinitions(HttpServer *server, ShortLived WebPluginListElt *webPluginListHead = NULL; WebPluginListElt *webPluginListTail = NULL; + unsigned int idMultiplier = 1; /* multiplier for the dynamic logging id */ zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_DEBUG2, "%s begin dirname %s\n", __FUNCTION__, dirname); if (directory) { while ((entriesRead = directoryRead(directory, directoryDataBuffer, DIR_BUFFER_SIZE, &returnCode, &reasonCode)) > 0) { @@ -596,10 +628,12 @@ static WebPluginListElt* readWebPluginDefinitions(HttpServer *server, ShortLived if (jsonObject) { char *identifier = jsonObjectGetString(jsonObject, "identifier"); char *pluginLocation = jsonObjectGetString(jsonObject, "pluginLocation"); + int pluginLogLevel = checkLoggingVerbosity(serverConfigFile, identifier, slh); if (identifier && pluginLocation) { JsonObject *pluginDefinition = readPluginDefinition(slh, identifier, pluginLocation); if (pluginDefinition) { - WebPlugin *plugin = makeWebPlugin(pluginLocation, pluginDefinition, internalAPIMap); + WebPlugin *plugin = makeWebPlugin(pluginLocation, pluginDefinition, internalAPIMap, + &idMultiplier, pluginLogLevel); if (plugin != NULL) { initalizeWebPlugin(plugin, server); //zlux does this, so don't bother doing it twice. @@ -660,13 +694,13 @@ void checkAndSetVariable(JsonObject *mvdSettings, } } -static void initLoggingComponents() { +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); zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, "zssServer startup, version %s\n", productVersion); } -static void initVersionComponents(){ +static void initVersionComponents(void){ sprintf(productVersion,"%d.%d.%d+%d",PRODUCT_MAJOR_VERSION,PRODUCT_MINOR_VERSION,PRODUCT_REVISION,PRODUCT_VERSION_DATE_STAMP); } @@ -818,6 +852,116 @@ static int validateFilePermissions(const char *filePath) { #endif /* ZSS_IGNORE_PERMISSION_PROBLEMS */ +/* + "agent": { + ..., + "jwt": { + "enabled": true, + "fallback": true, + "key": { + "storeType": "pkcs11", //optional, since only one type is supported + "name": "ZOWE.ZSS.APIMLQA", + "keyId": "jwtsecret" //optional + } + } + } + + If `jwtKeystore` is present in the config, this will initialize the server + to use the specified keystore and public key, with or without fallback to + session tokens, as specified. + + Otherwise we would just use session tokens. + */ +int initializeJwtKeystoreIfConfigured(JsonObject *const serverConfig, + HttpServer *const httpServer) { + JsonObject *const agentSettings = jsonObjectGetObject(serverConfig, "agent"); + if (agentSettings == NULL) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_INFO, + "Will not accept JWTs: agent configuration missing\n"); + return 0; + } + + JsonObject *const jwtSettings = jsonObjectGetObject(agentSettings, "jwt"); + if (jwtSettings == NULL) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_INFO, + "Will not accept JWTs: JWT keystore configuration missing\n"); + return 0; + } + + if (!jsonObjectGetBoolean(jwtSettings, "enabled")) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_INFO, + "Will not accept JWTs: disabled in the configuration\n"); + return 0; + } + + const int fallback = jsonObjectGetBoolean(jwtSettings, "fallback"); + + JsonObject *const jwtKeyConfig = jsonObjectGetObject(jwtSettings, "key"); + if (jwtKeyConfig == NULL) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_SEVERE, + "JWT keystore configuration missing\n"); + return 1; + } + + const char *const keystoreType = jsonObjectGetString(jwtKeyConfig, "type"); + const char *const keystoreName = jsonObjectGetString(jwtKeyConfig, "name"); + const char *const keyId = + jsonObjectHasKey(jwtKeyConfig, "keyId")? + jsonObjectGetString(jwtKeyConfig, "keyId") + : "jwtsecret"; + + if (keystoreType != NULL && strcmp(keystoreType, "pkcs11") != 0) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_SEVERE, + "Invalid JWT configuration: unknown keystore type %s\n", keystoreType); + return 1; + } else if (keystoreName == NULL) { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_SEVERE, + "Invalid JWT configuration: keystore name missing\n"); + return 1; + } else { + zowelog(NULL, + LOG_COMP_ID_MVD_SERVER, + ZOWE_LOG_INFO, + "Will use JWT using PKCS#11 token '%s', key id '%s'," + " %s fallback to legacy tokens\n", + keystoreName, + keyId, + fallback? "with" : "without"); + } + + + int initTokenRc, p11rc, p11Rsn; + const int contextInitRc = httpServerInitJwtContext(httpServer, + fallback, + keystoreName, + keyId, + CKO_PUBLIC_KEY, + &initTokenRc, &p11rc, &p11Rsn); + if (contextInitRc != 0) { + zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_WARNING, + "Server startup problem: could not load the JWT key %s from token %s:" + " rc %d, p11rc %d, p11Rsn %d\n", + keyId, + keystoreName, + initTokenRc, p11rc, p11Rsn); + return 1; + } + + return 0; +} + int main(int argc, char **argv){ if (argc == 1) { printf("Usage: zssServer \n"); @@ -890,9 +1034,12 @@ int main(int argc, char **argv){ zowelog(NULL, LOG_COMP_ID_MVD_SERVER, ZOWE_LOG_INFO, "ZSS server settings: address=%s, port=%d\n", address, port); server = makeHttpServer2(base,inetAddress,port,requiredTLSFlag,&returnCode,&reasonCode); if (server){ + if (0 != initializeJwtKeystoreIfConfigured(mvdSettings, server)) { + return 8; + } server->defaultProductURLPrefix = PRODUCT; loadWebServerConfig(server, mvdSettings); - readWebPluginDefinitions(server, slh, pluginsDir); + readWebPluginDefinitions(server, slh, pluginsDir, serverConfigFile); installUnixFileContentsService(server); installUnixFileRenameService(server); installUnixFileCopyService(server); diff --git a/deps/zowe-common-c b/deps/zowe-common-c index adaa9c630..8424d0c23 160000 --- a/deps/zowe-common-c +++ b/deps/zowe-common-c @@ -1 +1 @@ -Subproject commit adaa9c6304744b33c8bc2297f2eaa47853bd0da1 +Subproject commit 8424d0c232ec415a659695971e9f7fa893dd501e diff --git a/h/zis/services/secmgmt.h b/h/zis/services/secmgmt.h index 64f2c698f..43ac84822 100644 --- a/h/zis/services/secmgmt.h +++ b/h/zis/services/secmgmt.h @@ -19,10 +19,13 @@ /*** Common security structs and definitions ***/ #define ZIS_SECURITY_CLASS_MAX_LENGTH 8 -#define ZIS_SECURITY_PROFILE_MAX_LENGTH 246 +#define ZIS_SECURITY_PROFILE_MAX_LENGTH 255 /* 246->255: Top Secret has a max profile of 255 and needs to use this struct. */ #define ZIS_SECURITY_GROUP_MAX_LENGTH 8 #define ZIS_USER_ID_MAX_LENGTH 8 +#define ZIS_PARMLIB_PARM_SECMGMT_USER_CLASS CMS_PROD_ID".SECMGMT.CLASS" +#define ZIS_PARMLIB_PARM_SECMGMT_AUTORESFRESH CMS_PROD_ID".SECMGMT.AUTOREFRESH" + typedef struct SAFStatus_tag { int safRC; int racfRC; @@ -62,7 +65,7 @@ ZOWE_PRAGMA_PACK typedef struct ZISUserProfileEntry_tag { char userID[8]; char defaultGroup[8]; - char name[20]; + char name[32]; /* 20->32: Top Secret has a max name of 32 and needs to use this struct. */ } ZISUserProfileEntry; typedef struct ZISUserProfileServiceParmList_tag { @@ -91,6 +94,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisUserProfilesServiceFunction, "ZISSXUPR") int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateUserProfileParmList(ZISUserProfileServiceParmList *parm); #define RC_ZIS_UPRFSRV_OK 0 #define RC_ZIS_UPRFSRV_PARMLIST_NULL 8 @@ -101,7 +105,8 @@ int zisUserProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_UPRFSRV_IMPERSONATION_MISSING 13 #define RC_ZIS_UPRFSRV_INTERNAL_SERVICE_FAILED 14 #define RC_ZIS_UPRFSRV_ALLOC_FAILED 15 -#define RC_ZIS_UPRFSRV_MAX_RC 15 +#define RC_ZIS_UPRFSRV_UNSUPPORTED_ESM 16 +#define RC_ZIS_UPRFSRV_MAX_RC 16 /*** Genres profile service ***/ @@ -141,6 +146,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGenresProfilesServiceFunction, "ZISSXGRP") int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGenresProfileParmList(ZISGenresProfileServiceParmList *parm); #define RC_ZIS_GRPRFSRV_OK 0 #define RC_ZIS_GRPRFSRV_PARMLIST_NULL 8 @@ -153,7 +159,8 @@ int zisGenresProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_GRPRFSRV_INTERNAL_SERVICE_FAILED 16 #define RC_ZIS_GRPRFSRV_ALLOC_FAILED 17 #define RC_ZIS_GRPRFSRV_USER_CLASS_NOT_READ 18 -#define RC_ZIS_GRPRFSRV_MAX_RC 18 +#define RC_ZIS_GRPRFSRV_UNSUPPORTED_ESM 19 +#define RC_ZIS_GRPRFSRV_MAX_RC 19 /*** General resource access list service ***/ @@ -194,6 +201,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGenresAccessListServiceFunction, "ZISSXGAL") int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGenresAccessListParmList(ZISGenresAccessListServiceParmList *parm); #define RC_ZIS_ACSLSRV_OK 0 #define RC_ZIS_ACSLSRV_PARMLIST_NULL 8 @@ -206,7 +214,8 @@ int zisGenresAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_ACSLSRV_INTERNAL_SERVICE_FAILED 15 #define RC_ZIS_ACSLSRV_INSUFFICIENT_SPACE 16 #define RC_ZIS_ACSLSRV_USER_CLASS_NOT_READ 17 -#define RC_ZIS_ACSLSRV_MAX_RC 17 +#define RC_ZIS_ACSLSRV_UNSUPPORTED_ESM 18 +#define RC_ZIS_ACSLSRV_MAX_RC 18 /*** General resource profile administration service ***/ @@ -229,7 +238,8 @@ typedef enum ZISGenresAdminServiceAccessType_tag { ZIS_GENRES_ADMIN_ACESS_TYPE_READ = 0, ZIS_GENRES_ADMIN_ACESS_TYPE_UPDATE = 1, ZIS_GENRES_ADMIN_ACESS_TYPE_ALTER = 2, - ZIS_GENRES_ADMIN_ACESS_TYPE_MAX = 2, + ZIS_GENRES_ADMIN_ACESS_TYPE_ALL = 3, /* Top Secret doesn't have alter. */ + ZIS_GENRES_ADMIN_ACESS_TYPE_MAX = 3, } ZISGenresAdminServiceAccessType; #pragma enum(reset) @@ -276,6 +286,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGenresProfileAdminServiceFunction, "ZISSXPAA") int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGenresParmList(ZISGenresAdminServiceParmList *parmList); #define RC_ZIS_GSADMNSRV_OK 0 #define RC_ZIS_GSADMNSRV_PARMLIST_NULL 8 @@ -293,7 +304,8 @@ int zisGenresProfileAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea #define RC_ZIS_GSADMNSRV_USER_CLASS_NOT_READ 20 #define RC_ZIS_GSADMNSRV_AUTOREFRESH_NOT_READ 21 #define RC_ZIS_GSADMNSRV_OWNER_TOO_LONG 22 -#define RC_ZIS_GSADMNSRV_MAX_RC 22 +#define RC_ZIS_GSADMNSRV_UNSUPPORTED_ESM 23 +#define RC_ZIS_GSADMNSRV_MAX_RC 23 /*** Group profile service ***/ @@ -333,6 +345,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGroupProfilesServiceFunction, "ZISSXGPP") int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGroupProfileParmList(ZISGroupProfileServiceParmList *parm); #define RC_ZIS_GPPRFSRV_OK 0 #define RC_ZIS_GPPRFSRV_PARMLIST_NULL 8 @@ -343,6 +356,7 @@ int zisGroupProfilesServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_GPPRFSRV_IMPERSONATION_MISSING 13 #define RC_ZIS_GPPRFSRV_INTERNAL_SERVICE_FAILED 14 #define RC_ZIS_GPPRFSRV_ALLOC_FAILED 15 +#define RC_ZIS_GPPRFSRV_UNSUPPORTED_ESM 16 #define RC_ZIS_GPPRFSRV_MAX_RC 16 /*** Group access list service ***/ @@ -384,6 +398,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGroupAccessListServiceFunction, "ZISSXGPA") int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGroupAccessListParmList(ZISGroupAccessListServiceParmList *parm); #define RC_ZIS_GRPALSRV_OK 0 #define RC_ZIS_GRPALSRV_PARMLIST_NULL 8 @@ -394,7 +409,8 @@ int zisGroupAccessListServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_GRPALSRV_ALLOC_FAILED 13 #define RC_ZIS_GRPALSRV_INTERNAL_SERVICE_FAILED 14 #define RC_ZIS_GRPALSRV_INSUFFICIENT_SPACE 15 -#define RC_ZIS_GRPALSRV_MAX_RC 15 +#define RC_ZIS_GRPALSRV_UNSUPPORTED_ESM 16 +#define RC_ZIS_GRPALSRV_MAX_RC 16 /*** Group administration service ***/ @@ -467,6 +483,7 @@ ZOWE_PRAGMA_PACK_RESET #pragma map(zisGroupAdminServiceFunction, "ZISSXPGA") int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, CrossMemoryService *service, void *parm); +int validateGroupParmList(ZISGroupAdminServiceParmList *parmList); #define RC_ZIS_GRPASRV_OK 0 #define RC_ZIS_GRPASRV_PARMLIST_NULL 8 @@ -483,7 +500,8 @@ int zisGroupAdminServiceFunction(CrossMemoryServerGlobalArea *globalArea, #define RC_ZIS_GRPASRV_USER_CLASS_TOO_LONG 19 #define RC_ZIS_GRPASRV_USER_CLASS_NOT_READ 20 #define RC_ZIS_GRPASRV_AUTOREFRESH_NOT_READ 21 -#define RC_ZIS_GRPASRV_MAX_RC 21 +#define RC_ZIS_GRPASRV_UNSUPPORTED_ESM 22 +#define RC_ZIS_GRPASRV_MAX_RC 22 #endif /* ZIS_SERVICES_SECMGMT_H_ */ diff --git a/h/zis/services/secmgmtUtils.h b/h/zis/services/secmgmtUtils.h new file mode 100644 index 000000000..9afe160fa --- /dev/null +++ b/h/zis/services/secmgmtUtils.h @@ -0,0 +1,30 @@ + + +/* + 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 ZIS_SERVICES_SECMGMTUTILS_H_ +#define ZIS_SERVICES_SECMGMTUTILS_H_ + +#include "radmin.h" + +bool secmgmtGetCallerUserID(RadminUserID *caller); + +#endif + +/* + 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/h/zis/services/secmgmttss.h b/h/zis/services/secmgmttss.h new file mode 100644 index 000000000..5b0bb630e --- /dev/null +++ b/h/zis/services/secmgmttss.h @@ -0,0 +1,47 @@ + + +/* + 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 ZIS_SERVICES_SECMGMTTSS_H_ +#define ZIS_SERVICES_SECMGMTTSS_H_ + +int zisUserProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGroupProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGenresProfilesServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGenresAccessListServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGroupAccessListServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGroupAdminServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +int zisGenresProfileAdminServiceFunctionTSS(CrossMemoryServerGlobalArea *globalArea, + CrossMemoryService *service, void *parm); + +#endif + +/* + 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/h/zis/services/tssparsing.h b/h/zis/services/tssparsing.h new file mode 100644 index 000000000..2387fb2bd --- /dev/null +++ b/h/zis/services/tssparsing.h @@ -0,0 +1,80 @@ + + +/* + 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 TSS_PARSING_H_ +#define TSS_PARSING_H_ + +#define TSS_KEY_DELIMITER " = " +#define TSS_KEY_DELIMITER_LEN 3 + +#define TSS_ADMIN_INDICATOR "-SC" +#define TSS_ADMIN_INDICATOR_LEN 3 + +#define TSS_MAX_KEY_LENGTH 10 +#define TSS_MAX_NUM_OF_KEYS 2 + +#define TSS_FUNCTION_END "TSS" +#define TSS_MESSAGE_ID_MAX_LEN 8 +#define TSS_SERVICE_MSG_MAX_LEN 17 +#define TSS_COMMAND_SUCCESS "TSS0300I" +#define TSS_COMMAND_FAILURE "TSS0301I" + +#define TSS_MAX_VALUE_LEN 255 + +/* Search Keys */ +#define TSS_ACCESSOR_ID_KEY "ACCESSORID" +#define TSS_DEFAULT_GROUP_KEY "DFLTGRP" +#define TSS_NAME_KEY "NAME" +#define TSS_DEPARTMENT_KEY "DEPT ACID" +#define TSS_OWNER_KEY "OWNER(" +#define TSS_XAUTH_KEY "XAUTH" +#define TSS_ACID_KEY "ACID(" +#define TSS_ACCESS_KEY "ACCESS" +#define TSS_ACIDS_KEY "ACIDS" + +/* Stores information about a specific + * key. + */ +typedef struct TSSKeyData_t { + unsigned int startPos; + unsigned int endPos; + unsigned int delimPos; + char keyName[TSS_MAX_KEY_LENGTH]; + int keyLength; +} TSSKeyData; + +bool tssIsLineBlank(const char *str, unsigned int len); + +bool tssIsErrorDetected(const char *str, unsigned int len); + +bool tssIsOutputDone(const char *str, unsigned int len); + +int tssGetActualValueLength(const char *str, unsigned int len); + +int tssParseLineForKeys(const char *str, unsigned int len, TSSKeyData *keys); + +int tssFindValueForKey(const char *str, unsigned int len, TSSKeyData *keys, int keyIndex, + int numberOfKeys, char *valueBuffer, unsigned int valueBufferSize); + +int tssFindDesiredKey(const char *searchStr, TSSKeyData *keys); + +#endif + +/* + 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. +*/