Skip to content

Commit

Permalink
Merge pull request #70 from ambitus/feat/ras_security_updates
Browse files Browse the repository at this point in the history
RAS Update
  • Loading branch information
ElijahSwiftIBM authored Feb 13, 2024
2 parents 8d96ac1 + bebedbb commit dd23a06
Show file tree
Hide file tree
Showing 81 changed files with 2,495 additions and 556 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def clean_python_environment() {
def install_poetry(python) {
echo "Installing Poetry..."

sh "bash -c 'curl -sSL https://install.python-poetry.org | ${python} -'"
sh "bash -c 'curl -sSL https://install.python-poetry.org' | ${python} -"
}

def build_poetry_environment(python) {
Expand Down
19 changes: 10 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[build-system]
build-backend = "poetry.core.masonry.api"
requires = ["poetry-core>=1.7.0", "setuptools>=61"]
requires = ["poetry-core>=1.9.0", "setuptools>=69"]

[tool.poetry]
name="pyracf"
version="1.0b4"
version="1.0b5"
description="Python interface to RACF using IRRSMO00 RACF Callable Service."
license = "Apache-2.0"
authors = [
Expand Down Expand Up @@ -44,13 +44,14 @@
defusedxml = ">=0.7.1"

[tool.poetry.group.dev.dependencies]
isort = ">=5.12.0"
pre-commit = ">=3.4.0"
black = ">=23.9.1"
flake8 = ">=6.1.0"
pylint = ">=3.0.0"
coverage = ">=7.3.2"
wheel = ">=0.41.2"
isort = ">=5.13.2"
pre-commit = ">=3.6.0"
black = ">=24.1.1"
flake8 = ">=7.0.0"
pylint = ">=3.0.3"
coverage = ">=7.4.1"
wheel = ">=0.42.0"
ebcdic = ">=1.1.1"

[tool.isort]
profile = "black"
Expand Down
15 changes: 8 additions & 7 deletions pyracf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Make security admin subclasses available from package root."""

from .access.access_admin import AccessAdmin
from .common.add_operation_error import AddOperationError
from .common.alter_operation_error import AlterOperationError
from .common.downstream_fatal_error import DownstreamFatalError
from .common.security_request_error import SecurityRequestError
from .common.segment_error import SegmentError
from .common.segment_trait_error import SegmentTraitError
from .common.userid_error import UserIdError
from .common.exceptions.add_operation_error import AddOperationError
from .common.exceptions.alter_operation_error import AlterOperationError
from .common.exceptions.downstream_fatal_error import DownstreamFatalError
from .common.exceptions.security_request_error import SecurityRequestError
from .common.exceptions.segment_error import SegmentError
from .common.exceptions.segment_trait_error import SegmentTraitError
from .common.exceptions.userid_error import UserIdError
from .connection.connection_admin import ConnectionAdmin
from .data_set.data_set_admin import DataSetAdmin
from .group.group_admin import GroupAdmin
Expand Down
4 changes: 4 additions & 0 deletions pyracf/access/access_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class AccessAdmin(SecurityAdmin):

def __init__(
self,
irrsmo00_result_buffer_size: Union[int, None] = None,
debug: bool = False,
dump_mode: bool = False,
generate_requests_only: bool = False,
update_existing_segment_traits: Union[dict, None] = None,
replace_existing_segment_traits: Union[dict, None] = None,
Expand Down Expand Up @@ -44,7 +46,9 @@ def __init__(
}
super().__init__(
"permission",
irrsmo00_result_buffer_size=irrsmo00_result_buffer_size,
debug=debug,
dump_mode=dump_mode,
generate_requests_only=generate_requests_only,
update_existing_segment_traits=update_existing_segment_traits,
replace_existing_segment_traits=replace_existing_segment_traits,
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Exception to use when IRRSMO00 is unable to process a request."""

from typing import Union


Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
206 changes: 130 additions & 76 deletions pyracf/common/irrsmo00.c
Original file line number Diff line number Diff line change
@@ -1,107 +1,161 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE (100000)

#pragma linkage(IRRSMO64, OS)

typedef struct {
unsigned char len;
char str[8];
} VarStr_T;

// This function changes any null character not preceded by '>' to a blank character.
// This is a workaround for an issue where profile data embedded in response xml
// returned by IRROSMO00 sometimes includes null characters instead of properly
// encoded text, which causes the returned xml to be truncated.
void null_byte_fix(char* str, unsigned int str_len) {
for (int i = 1; i < str_len; i++){
if (str[i] == 0) {
if (str[i-1] == 0x6E) {
return;
}
else {
str[i] = 0x40;
}
}
}
}

static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) {
const unsigned int xml_len;
const unsigned int input_opts;
const uint8_t input_userid_len;
const char *input_xml;
const char *input_userid;

static char *kwlist[] = {"xml_str", "xml_len", "opts", "userid", "userid_len", NULL};
typedef struct
{
unsigned char running_userid_length;
char running_userid[8];
} running_userid_t;

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y|IIyb", kwlist, &input_xml, &xml_len, &input_opts, &input_userid, &input_userid_len)) {
static PyObject *call_irrsmo00(PyObject *self, PyObject *args, PyObject *kwargs)
{
const char *request_xml;
const unsigned int request_xml_length;
const unsigned int result_buffer_size;
const unsigned int irrsmo00_options;
const char *running_userid;
const uint8_t running_userid_length;

static char *kwlist[] = {
"request_xml",
"request_xml_length",
"result_buffer_size",
"irrsmo00_options",
"running_userid",
"running_userid_length",
NULL};

if (
!PyArg_ParseTupleAndKeywords(
args,
kwargs,
"y|IIIyb",
kwlist,
&request_xml,
&request_xml_length,
&result_buffer_size,
&irrsmo00_options,
&running_userid,
&running_userid_length))
{
return NULL;
}


char work_area[1024];
char req_handle[64] = { 0 };
VarStr_T userid = { input_userid_len, {0}};
char req_handle[64] = {0};
running_userid_t running_userid_struct = {running_userid_length, {0}};
unsigned int alet = 0;
unsigned int acee = 0;
unsigned char rsp[BUFFER_SIZE+1];
memset(rsp, 0, BUFFER_SIZE);
unsigned int saf_rc=0, racf_rc=0, racf_rsn=0;
unsigned int num_parms=17, fn=1, opts = input_opts, rsp_len = sizeof(rsp)-1;

strncpy(userid.str, input_userid, userid.len);
unsigned char result_buffer[result_buffer_size];
memset(result_buffer, 0, result_buffer_size);
unsigned int saf_rc = 0;
unsigned int racf_rc = 0;
unsigned int racf_rsn = 0;
unsigned int num_parms = 17;
unsigned int fn = 1;

strncpy(
running_userid_struct.running_userid,
running_userid,
running_userid_struct.running_userid_length);

IRRSMO64(
work_area,
alet,
&saf_rc,
alet,
&racf_rc,
alet,
&racf_rsn,
num_parms,
fn,
opts,
xml_len,
input_xml,
req_handle,
userid,
acee,
rsp_len,
rsp
);

null_byte_fix(rsp,rsp_len);

return Py_BuildValue("yBBB", rsp, saf_rc, racf_rc, racf_rsn);
work_area,
alet,
&saf_rc,
alet,
&racf_rc,
alet,
&racf_rsn,
num_parms,
fn,
irrsmo00_options,
request_xml_length,
request_xml,
req_handle,
running_userid_struct,
acee,
result_buffer_size,
result_buffer);

// https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
//
// According to the Python 3 C API documentation:
// When memory buffers are passed as parameters to supply data to
// build objects, as for the s and s# formats, the required data is
// copied. Buffers provided by the caller are never referenced by
// the objects created by Py_BuildValue(). In other words, if your
// code invokes malloc() and passes the allocated memory to
// Py_BuildValue(), your code is responsible for calling free() for
// that memory once Py_BuildValue() returns.
//
// y# (bytes) [const char *, Py_ssize_t]
// This converts a C string and its lengths to a Python object.
// If the C string pointer is NULL, None is returned.
//
// https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
//
// So, given that 'result_buffer' is a stack allocated buffer
// and that Python creates a copy of the buffer, which Python's
// garbage collection should be responsible for, we do not need
// to do any memory mangement here. 'result_buffer' will simply
// just be popped off the stack when this function returns.
//
// Also, according to the Python3 C API documentation, 'y#' should
// be just giving us a copy copy of exactly what is in the buffer,
// without attempting to do any transformations to the data.
// The following GeesForGeeks article futher confirms that we are
// going to get a bytes object that is completely unmanipulated.
// https://www.geeksforgeeks.org/c-strings-conversion-to-python/
//
// In this case, all post processing of the data is handled on
// the Python side.
//
// Also note that when two or more return values are provided,
// Py_BuildValue() will return a Tuple.

return Py_BuildValue(
"y#BBB",
result_buffer,
result_buffer_size,
saf_rc,
racf_rc,
racf_rsn);
}

static char call_irrsmo00_docs[] =
"call_irrsmo00(input_xml: bytes, xml_len: uint, opts: uint): Returns an XML response string and return and reason codes from the IRRSMO00 RACF Callable Service.\n";
"call_irrsmo00(\n"
" request_xml: bytes,\n"
" request_xml_length: uint,\n"
" result_buffer_size: uint,\n"
" irrsmo00_options: uint,\n"
" running_userid: bytes,\n"
" running_userid_length: uint,\n"
") -> List[bytes,int,int,int]:\n"
"# Returns an XML result string and return and reason "
"codes from the IRRSMO00 RACF Callable Service.\n";

static PyMethodDef cpyracf_methods[] = {
{"call_irrsmo00", (PyCFunction)call_irrsmo00,
METH_VARARGS | METH_KEYWORDS, call_irrsmo00_docs},
{NULL}
};
{"call_irrsmo00", (PyCFunction)call_irrsmo00,
METH_VARARGS | METH_KEYWORDS, call_irrsmo00_docs},
{NULL}};

static struct PyModuleDef cpyracf_module_def =
{
static struct PyModuleDef cpyracf_module_def = {
PyModuleDef_HEAD_INIT,
"cpyracf",
"cpyracf",
"C code that enables pyRACF to call the IRRSMO00 RACF callable service.\n",
-1,
cpyracf_methods
};
cpyracf_methods};

PyMODINIT_FUNC PyInit_cpyracf(void)
{
Py_Initialize();
return PyModule_Create(&cpyracf_module_def);
Py_Initialize();
return PyModule_Create(&cpyracf_module_def);
}
Loading

0 comments on commit dd23a06

Please sign in to comment.