I was trying to recreate the commands for the MQ certificates and keyring, so I could create a new set for a second queue manager.
At first glance it looked like writing it in rexx would involve a lot of parsing, so I started using the RACF callable service to extract information certificate and keyring information from RACF using a C/assembler interface.
This was a slow journey with many pitfalls, and I ended up using a REXX program.
I’ve put the code up on git hub for both the RACF and the C interface, to help other people who may be considering going into the RACF callable services swamp.
Using the C interface
You use the R_datalib or IRRSDL00 callable service.
The key bits of code are
#pragma linkage(IRRSDL00 ,OS)
- - - - - - - - - - - - - - - - -
char * workarea = (char *) malloc(1024) ;
- - - - - - - - - - - - - - - - -
struct {
char length;
char value[8];
} RACF_userid;
memcpy(RACF_userid.value,"START1 ",8);
RACF_userid.length = 6;
- - - - - - - - - - - - - - - - -
int parmlist_version = 0;
- - - - - - - - - - - - - - - - -
char DataGetFirst = 0x01 ; /*Data getFirst */
int attributes = 0;
rc= IRRSDL00( workarea, // WORKAREA
&ALET1 , // ALET
&SAF_RC, // SAF RC
&ALET2, // ALET
&RACF_RC,// RACF RC
&ALET3 , // ALET
&RACF_RS,// RACF Reason
&DataGetFirst ,// function code
&attributes, // option
&RACF_userid, // RACF userid
&ring_name, // certificate fw ... cert
&parmlist_version, // Aplication userid
&parmlist );
That code was not particularly difficult – it got more difficult using the parmlist.
#pragma pack(1)
struct {
char * results_handle ; //in offset 0
int certificate_usage ; //out offset 4
int isDefault ; // out 8
int certificate_length; //in/out c
char * certificate ; // in 10
int private_key_length; //in/out 14
char * private_key ; //in 18
int private_key_type ; //out 1c
int private_bitsize ; //out 20
int label_length ; //in/out 24
char * label ; //in 28
char cert_useridl; // in 2c
char cert_userid[8]; // in 2d
char temp[3] ; // offset 35
int subjects_dn_length ; //in 38
char * subjects_dn_ptr ; //in 3c
int record_length ; //in/out 40
char * record_ptr ; //inp 44
int cert_status ; //in/out 48
} parmlist;
#pragma pack(4)
- - -
char certificate[2000];
parmlist.certificate_length = 2000 ;
parmlist.certificate =( char *) &certificate;
Notes:
- The temp[3] is not in the documentation and I got various errors without it – such as internal error. This was because the pointers following it were at the wrong offset.
- parmlist.cert_useridl must be set to 8, and the value must be padded on the right with blanks. This is different to the RACF_userid field in IRRSDL00 which is the true length of the userid.
Compile it
//S1 JCLLIB ORDER=CBC.SCCNPRC
//DOCLG EXEC PROC=EDCCBG,INFILE='ADCD.C.SOURCE(C)',
// CPARM='OPTF(DD:COPTS)'
//* CPARM='LIST,SSCOMM,SOURCE,LANGLVL(EXTENDED)'
//COMPILE.COPTS DD *
LIST,SSCOMM,SOURCE,LANGLVL(EXTENDED)
aggregate(offsethex) xref
TEST
/*
//COMPILE.SYSIN DD *
...
//BIND.SYSLMOD DD DISP=SHR,DSN=ADCD.PDSE.LOADLIB(CERT)
//BIND.CSS DD DISP=SHR,DSN=SYS1.CSSLIB
//BIND.SYSIN DD *
INCLUDE CSS(IRRSDL00)
/*
The output
The subject ( what I was after) had a format like in hex and ASCII is
00000000 : 3038310B 30090603 55040613 02474231 081.0...U....GB1
00000010 : 0C300A06 0355040A 0C035353 53310B30 .0...U....SSS1.0
00000020 : 09060355 040B0C02 4341310E 300C0603 ...U....CA1.0...
00000030 : 5504030C 05535343 4138 U....SSCA8
If I list the certificate it reports Subject’s Name: CN=SSCA8.OU=CA.O=SSS.C=GB, you can see the elements in the hex dump.
The data is DER encoding of ANS.1 format. Buried within this, is a field with value “GB” which is a type 2.5.4.6 which is country. Information is stored in certificate in ASCII and in DER format, so I was not surprised to see the data in this format.
The easiest way of decoding this is to use gskit (IBM global security kit). Gskit provides c routines for encryption/decryption and management of certificates.
Using GSKIT
Change the compile JCL
//S1 JCLLIB ORDER=CBC.SCCNPRC
//DOCLG EXEC PROC=EDCCBG,INFILE='ADCD.C.SOURCE(C)',
// CPARM='OPTF(DD:COPTS)',
// GREGSIZ='0M'
//COMPILE.COPTS DD *
LIST,SSCOMM,SOURCE,LANGLVL(EXTENDED)
TEST
LSEARCH(/usr/lpp/gskssl/include/)
SEARCH(//'CEE.SCEEH.+',/usr/lpp/gskssl/include/)
DEFINE(MVS),LONGNAME,RENT,DLL,EXPMAC
aggregate(offsethex) xref
//COMPILE.SYSIN DD *
#pragma runopts(POSIX(ON))
..
//BIND.SYSLMOD DD DISP=SHR,DSN=ADCD.PDSE.LOADLIB(CERT)
//BIND.IMP DD DISP=SHR,DSN=SYS1.SIEASID
//BIND.CSS DD DISP=SHR,DSN=SYS1.CSSLIB
//BIND.SYSIN DD *
INCLUDE CSS(IRRSDL00)
include imp(GSKSSL)
include imp(GSKCMS31)
/*
How to decode the returned data
Gskit uses a gsk_buffer structure to handle strings. It is easy to use as it has
- length – the length of the string
- data – a pointer to the string
If gskit returns a gsk_buffer to your program, you should use gsk_free_buffer(…) to release the buffer when you have finished with it.
gsk_buffer name;
x509_name out;
name.length = parmlist.subjects_dn_length;
name.data = parmlist.subjects_dn_ptr;
gskrc = gsk_decode_name(& name, &out);
if ( gskrc != 0)
printf("gsk_decode_name %s\n",gsk_strerror(gskrc));
All this routine does it to convert the ANS.1 encoded string, into a structure of elements. You can then use the following to print it
gskrc = gsk_name_to_dn(&out,&pName);
if ( gskrc != 0)
printf("gsk_name_to_dn %s\n",gsk_strerror(gskrc));
else printf("Name:%s\n",pName);
This produced
Name:CN=SSCA8,OU=CA,O=SSS,C=GB
As an exercise I wrote some code to format some of the x509 value –
for ( int i = 0; i < out.u.dn.count;i++)
{
for (int j = 0;j < out.u.dn.rdns[i].count; j++ )
{
printf("Type %s ",px509_attribute_type(
out.u.dn.rdns[i].attributes[j].attributeType ));
char * p2 = out.u.dn.rdns[i]. attributes[j]. name.data;
int l = out.u.dn.rdns[i]. attributes[j]. name.length;
printf("%*.*s\n",l,l, fromascii(p2,l));
}
}
It took an hour or so to work out how to access the elements: out.u.dn.rdns[i].attributes[j].attributeType . I had to write a routine px509_attribute_type which took the attribute type and return a string such as “CN” etc. I already had a function fromascii which took and ASCII string and converted it to EBCDIC. Using gsk_name_to_dn was much easier.