, Tracer les connexions REXEC sur votre IBMi

Le REXEC est un protocole qui a été déclassifié pour des questions de sécurité depuis plusieurs années et qui ne devrait plus être utilisés mais il est possible qu’ils soit encore ouvert chez vous, c’est le port 512.
Voici une solution pour connaitre qui se connecte en REXEC sur votre partition IBM

1) Création d’une table pour la log des tentatives de REXEC

CREATE TABLE GTOOLS/CTLREXEC (REXIP CHAR ( 15) NOT NULL WITH
DEFAULT, REXUSR CHAR ( 10) NOT NULL WITH DEFAULT, REXDAT DATE NOT
NULL WITH DEFAULT, REXTIM TIME NOT NULL WITH DEFAULT)

2) Création d’un Programme CLP

//
/* / / Exemple REXEC Server Logon exit program. / / PGM CTLREXEC / / / /*/

TSTLOGCL: PGM PARM(&APPIDIN &USRIN &USRLENIN &AUTIN &AUTLENIN &IPADDRIN &IPLENIN +
&RETCDOUT &USRPRFOUT &PASSWDOUT &CURLIBOUT)

/* Declare input parameters */

DCL VAR(&APPIDIN) TYPE(CHAR) LEN(4)

/* Application identifier */ /* 1 FTP server program */ /* 2 REXEC server program */

DCL VAR(&USRIN) TYPE(CHAR) LEN(999)

/* User ID */

DCL VAR(&USRLENIN) TYPE(CHAR) LEN(4)

/* Length of user ID */

DCL VAR(&AUTIN) TYPE(CHAR) LEN(999)

/* Authentication string */

DCL VAR(&AUTLENIN) TYPE(CHAR) LEN(4)

/* Length of auth. string */

DCL VAR(&IPADDRIN) TYPE(CHAR) LEN(15)

/* Client IP address */

DCL VAR(&IPLENIN) TYPE(CHAR) LEN(4)

/* IP address length */

DCL VAR(&RETCDOUT) TYPE(CHAR) LEN(4)

/* return code (out) / / 1 OK / / 0 KO */

DCL VAR(&USRPRFOUT) TYPE(CHAR) LEN(10)

/* user profile (out) */

DCL VAR(&PASSWDOUT) TYPE(CHAR) LEN(10)

/* password (out) */

       DCL        VAR(&CURLIBOUT)  TYPE(*CHAR) LEN(10) /* current library (out)   */

/* Declare local copies of parameters (in format usable by CL) */

DCL VAR(&APPID) TYPE(DEC) LEN(1 0)
DCL VAR(&USRLEN) TYPE(DEC) LEN(5 0)

DCL VAR(&AUTLEN) TYPE(DEC) LEN(5 0)
DCL VAR(&IPLEN) TYPE(*DEC) LEN(5 0)
MONMSG CPF0000 exec(goto fin)
addlible gtools
monmsg CPF2103
CHGJOB LOG(4 0 *SECLVL) LOGCLPGM(YES)

/* Assign input parameters to local copies * /

CHGVAR VAR(&APPID) VALUE(%BINARY(&APPIDIN))

CHGVAR VAR(&USRLEN) VALUE(%BINARY(&USRLENIN))

CHGVAR VAR(&AUTLEN) VALUE(%BINARY(&AUTLENIN))

CHGVAR VAR(&IPLEN) VALUE(%BINARY(&IPLENIN))

/* Renvoyer OK */

CHGVAR VAR(%BINARY(&RETCDOUT)) VALUE(1)

/* Loguer les informations */

         RUNSQL     SQL('INSERT INTO CTLREXEC VALUES(''' *TCAT +  
                    %SST(&IPADDRIN 1 &iplen) *TCAT ''', ''' +   
                    *TCAT %SST(&USRIN 1 &USRLEN) *TCAT ''', +   
                    current date, current time)') COMMIT(*NONE) 
         monmsg sql0000 exec(do)
         sndusrmsg  msg(&IPADDRIN *bcat %sst(&USRIN 1 10)) msgtype(*info)
         enddo
         goto       end

fin:
CHGVAR VAR(%BINARY(&RETCDOUT)) VALUE(1)
END: ENDPGM

3) Compiler le programme en adoption de droit par rapport à QSECOFR

4) Associer au programme d’exit QIBM_QTMX_SVR_LOGON

ENDTCPSVR *REXEC

ADDEXITPGM EXITPNT(QIBM_QTMX_SVR_LOGON) +
FORMAT(TCPL0100) PGMNBR(1) +
PGM(gtools/ctlrexec)

STRTCPSVR *REXEC

5) Pour le suivi

select * from gtools/ctlrexec

REXIP REXUSR REXDAT REXTIM
10.10.10.10 GAIA 2021-03-29 21:39:11
10.2.0.8 PLB 2021-03-29 22:06:36
10.2.0.8 PLB 2021-03-30 06:38:02
10.2.0.8 PLB 2021-03-30 06:40:47
10.2.0.8 QSECOFR 2021-03-30 06:42:40

, 5 actions pour remplacer efficacement SNA

Vous le savez SNA ne sera pas éternel, et il vous faudra trouver des solutions pour le remplacer, ces solutions sont en plus souvent plus performantes

Exemple : en remplaçant les fichiers de /qdls par des fichiers de l’ifs natif !

Il y a plusieurs solutions:

Vous pouvez par exemple analyser vos sources, cette méthode pose 2 problèmes.

  • Vous allez analyser de nombreux sources qui ne sont plus utilisés
  • A contrario, vous n’avez pas forcément tous les sources

Voici une méthode plus efficace et facile à mettre en œuvre, elle est basé sur la traçabilité des commandes !

1) Démarrer le contrôle d’audit

Vous devez démarrer l’audit de journal si ce n’est pas encore le cas
la valeur QAUDCTL doit avoir *OBJAUD

2) Démarrer l’analyse des commandes

Le but est de trouvé les traitements qui utilisent les commandes du monde SNA
et ensuite de mettre une solution de remplacement.

Pour avoir une idée précise, vous devez laisser tourner au moins 15 jours …

PGM
/* démarrer l’analyse sur les commandes SNA * /

CHGOBJAUD OBJ(SNDDST) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SAVRSTLIB) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SAVRSTOBJ) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SAVRST) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SAVRSTCHG) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SNDNETF) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SNDNETMSG) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(SNDNETSPLF) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(CPYTOPCD) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(CPYFRMPCD) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(STRPASTHR) OBJTYPE(*CMD) OBJAUD(*ALL)
CHGOBJAUD OBJ(ADDDIRE) OBJTYPE(*CMD) OBJAUD(*ALL)

CHGOBJAUD OBJ(QY2FTML) OBJTYPE(*PGM) OBJAUD(*ALL)


ENDPGM

3) Analyse des commandes

DSPJRN JRN(QAUDJRN) JRNCDE((T)) ENTTYP(CD)

Si voulez avoir un fichier mieux présenté pour les fichiers type CD

CRTDUPOBJ OBJ(QASYCDJ5)
FROMLIB(QSYS)
OBJTYPE(*FILE) TOLIB(GTOOLS) NEWOBJ(ANALYSE) CST(*NO)
TRG(*NO)

puis

DSPJRN JRN(QAUDJRN)
JRNCDE((T))
ENTTYP(CD)
OUTPUT(*OUTFILE) OUTFILFMT(TYPE5)
OUTFILE(GTOOLS/ANALYSE)
OUTMBR(*FIRST *ADD)

4) Arrêt de l’audit

PGM
/* arrêt de l’analyse sur les commandes SNA */

CHGOBJAUD OBJ(SNDDST) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SAVRSTLIB) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SAVRSTOBJ) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SAVRST) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SAVRSTCHG) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SNDNETF) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SNDNETMSG) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(SNDNETSPLF) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(CPYTOPCD) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(CPYFRMPCD) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(STRPASTHR) OBJTYPE(*CMD) OBJAUD(*NONE)
CHGOBJAUD OBJ(ADDDIRE) OBJTYPE(*CMD) OBJAUD(*NONE)

CHGOBJAUD OBJ(QY2FTML) OBJTYPE(*PGM) OBJAUD(*NONE)
ENDPGM

5) Supprimer les commandes incriminées

Les solutions de remplacement

SNDDST par SNDMSG ou SNDSMTPEMM
SAVRSTLIB en V7R4 par un passage en IP ou solution à base de FTP
SAVRSTOBJ  »
SAVRST  »
SAVRSTCHG  »
SNDNETF par SNDSMTPEMM ou solution à base de FTP
SNDNETMSG par SNDMSG ou SNDSMTPEMM
CPYTOPCD par CPYTOSTMSF ou CPYTOIMPF
CPYFRMPCD par CPYFRMSTMF ou CPYFRMIMPF
STRPASTHR par TELNET
ADDDIRE par ADDSMTPLE pour les envois de mail

CALL QY2FTML par FTP ou SAVRSTOBJ en IP

Et les cas particuliers

Vous n’avez pas les sources, par exemple sur un progiciel de production
Il faudra faire preuve de plus d’imagination, par exemple une commande avec le même nom et des paramètres identiques à la commande d’origine placées avant dans la liste de bibliothèque, et qui remplace l’action souhaitée par une action similaire à déterminer !

80 % des problèmes sont sur le SNDDST

, , , Les traitements asynchrones sur IBMi


Une définition simple, ce sont des traitements qui se déclenchent pour traiter des entrées d’une pile et qui ne renvoient pas de résultats directs au proccess émetteur

Il existe des produits spécifiques pour faire ça, sur l’IBMi le plus connu est bien sur mqseries .
Mais il est maintenant possible d’installer des produits open source comme RabbitMQ , etc…

Il existe également des solutions natives de la plateforme.

Parmi ces solutions il y a 2 méthodes

  • Cyclique ( toutes les 30 secondes)
  • A l’événement qui ne se déclencheront qu’en cas de nécessité (exemple triggers)

On ne présentera ici que les secondes.

Les triggers

Ce sont des programmes qu’on va enregistrer au niveau de la base de données, ils vont se déclencher sur un update, insert ou delete avant ou après l’action.

On peut les déclarer par commande ADDPFTRG ou par l’instruction SQL CREATE TRIGGER, on peut être plus précis sur l’exécution en SQL (Niveau zone modifiée par exemple)

Les DTAQs

Ce sont des files d’attentes que l’on crée par CRTDTAQ , on peut écrire dedans par API (QSNDDTAQ, QRCVDTAQ) ou par SQL QSYS2.SEND_DATA_QUEUE() et QSYS2.RECEIVE_DATA_QUEUE()

Pour la réception, on peut indiquer un temps négatif souvent -1, et le traitement se déclenchera quand il y aura un entrée dans la file !

soit dans le programme de traitement
CALL PGM(QRCVDTAQ) PARM(&DTAQNOM &DTAQBIB &DTAQLEN
&DATA &WAIT)
/* &wait = -1 */

Les msgqs en wait

historiquement, souvent utilisé, par exemple pour superviser la msgq qsysopr

dans le programme de traitement qui boucle sur cette instruction
RCVMSG … WAIT(*MAX)

Les fichiers à fin retardée

Le principe est le suivant, votre programme attendra un enregistrement quand il aura fini de lire les enregistrements trouvés

Avant le programme RPGLE
OVRDBF FILE(VOTREFIC) EOFDLY(99999)

dans le programme RPGLE

dou %eof() ;
….
read VOTREFIC ;

endif ;

Les watchers


Permettent d’analyser en temps réels des messages qui arrive dans les joblog, les historiques de log voir une autre file de message (MSGQ) .
Pour démarrer un watcher, on utilise la commande STRWCH
STRWCH SSNID(ANAWCH) WCHPGM(votre bib/votre programme) +
CALLWCHPGM(WCHEVT) WCHMSG((ALL))
WCHMSGQ((Votre bib/votre msgq))

votre programme reçoit 4 paramètres
L’option, la session, l’erreur et la donnée du message

exemple

/* Paramètres reçus */

DCL VAR(&WCHOPTION) TYPE(CHAR) LEN(10)

DCL VAR(&SESSIONID) TYPE(CHAR) LEN(10)
DCL VAR(&ERROR) TYPE(CHAR) LEN(10)

DCL VAR(&EVTDATA) TYPE(CHAR) LEN(1024)

Les programmes d’exits

C’est des actions système enregistrées que vous pouvez voir par la commande WRKREGINF, et seules les actions définies dans cette liste sont utilisables.

Pour ajouter un programme c’est soit la commande addexitpgm ou l’option 8 dans wrkreginf.

Le principe , on reçoit un buffer avec les données en cours et on renvoie status pour dire ok ou ko, vous pouvez dire OK systématiquement et traiter ou faire un contrôle d’autorisation applicatif

Exemple sur FTP

PGM PARM (& APPID & OPID & USRPRF & REMOTEIP & REMOTELEN & OPINFO & OPLEN & OK)
DCL & APPID * CHAR 4 /* ID D’APPLICATION, NUM BINAIRE */
DCL & OPID * CHAR 4 /* ID D’OPERATION, NUMERO BINAIRE */
DCL & OPNUM * 4 /* OPERATION ID, UTILISABLE DANS CL */
DCL & USRPRF * CHAR 10 /* PROFIL UTILISATEUR UTILISANT FTP */
DCL & REMOTEIP * CHAR 251 /* ADRESSE IP */
DCL & REMOTELEN * CHAR 4 /* LONGUEUR DU PARAMETRE PRECEDENT */
DCL & OPINFO * CHAR 251 /* INFORMATIONS SPECIFIQUES OP */
DCL & OPLEN * CHAR 4 /* LONGUEUR DU PARAMETRE PRECEDENT */
DCL & OK * CHAR 4 /* SIGNAL DE CONFIRMATION / / seulement utilisateur FTPUSR */
if cond(&USRPRF *ne ‘FTPUSR’) then(do)
chgvar &ok (X’00000001′)
enddo
ENDPGM

pour l’ajouter

ADDEXITPGM EXITPNT(QIBM_QTMF_SVR_LOGON)
FORMAT(TCPL0100) PGMNBR(1) PGM(VOTRELIB/VOTREPGM)

Vous devez arrêter le service FTP pour que cela soit pris en compte
ENDTCPSVR *FTP puis STRTCPSVR

Conclusions

Vous avez plusieurs solutions dans certains cas , et certaines sont plus à jour

les 4 à utiliser aujourd’hui sont
Les triggers pour la base de données, si possible en SQL
Les watchers pour les événements de log systèmes
Les progammes d’exit pour les actions Système, par exemple pour les connexions ODBC
Les dtaq pour gérer des piles de données applicatives

Tous ces programmes sont appelés souvent, ils doivent être donc optimisés et ils ne doivent pas planter pour éviter de bloquer la file !

, 5 petites astuces pour améliorer votre base de données

1) Vous désirez mettre des noms longs .

Vous avez un fichier pf, et pour les gens qui font des requetes dessus vous voulez mettre des noms plus signicatif

exemple remplacer num234 par numero_de_client

Vous ne voulez pas ou vous pas migrer vers une base en SQL , voila comment faire

Cette opération ne peut pas être faite en SQL
En effet ALTER TABLE FICHIER1 ALTER COLUMN ne permet pas de changer le nom de la colonne

C’est le mot clé Alias qui permet ca

Fichier avant

A R FICHIER1F
A*
A NUMCLI 5 0 COLHDG(‘Numéro’ ‘Client’)
A NOMCLI 30 COLHDG(‘Nom ‘ ‘Client’)
A PRECLI 30 COLHDG(‘Prénom’ ‘Client’)

Vous pouvez mettre des noms longs

fichier après, on ajoute les alias

A R FICHIER1F
A*
A NUMCLI 5 0 COLHDG(‘Numéro’ ‘Client’)
A ALIAS(NUMERO_CLIENT)
A NOMCLI 30 COLHDG(‘Nom ‘ ‘Client’)
A ALIAS(NOM_CLIENT)
A PRECLI 30 COLHDG(‘Prénom’ ‘Client’)
A ALIAS(PRENOM_CLIENT)

Pour ne pas perdre vos données vous pouvez faire un change pf

CHGPF FILE(GDATA/FICHIER1) SRCFILE(GDATA/QDDSSRC) SRCMBR(FICHIER1)

Cette opération ne change pas le niveau de format , pas de recompile de votre application

vous devrez avoir le source sur votre machine de prod pour faire un CHGPF

Si vous n’avez pas le source mais que vous voulez préparer les données pour de la BI par exemple
vous pouvez faire des vues qui seront utilisées à la place de vos PF !

Exemple

CREATE VIEW fichier_client AS
SELECT nomcli as nom_client,
numcli as numero_client,
precli as prenom_client
FROM fichier1

2) Vous voulez ajouter une zone à notre pf

L’opération peut se faire cette fois directement par SQL, même sur un PF

Exemple, vous voulez ajouter une zone mail à votre fichier

ALTER TABLE FICHIER1 ADD COLUMN MAIL_CLIENT FOR COLUMN MAIL
CHARACTER ( 50) CCSID 1147 NOT NULL WITH DEFAULT

Vous ne perdez pas les données, ni les fichiers qui pointent dessus
Mais attention comme en DDS vous changer le niveau de format donc vos programmes doivent être recompilés ou vous devez indiquer lvlchk(*no) sur vos fichiers (analyse à faire)
Vous restez en format DDS, vous ne pourrez pas ajouter tous les types de données
Attention il faudra reporter la zone dans votre DDS pour être cohérent

3) Contrôlez les données de vos fichiers

Si vous avez des fichiers PF, vous pouvez avoir des données erronées, vous pouvez maintenant simplement contrôler la validité de vos données par une fonction table, systools.validate_data !

select *
from table (
systools.validate_data(
library_name => ‘votrebib’, file_name => ‘FICHIER1’,
member_name => ‘*LAST’) )

Ça vous permettra d’avoir la liste des enregistrements qui ont des erreurs de données, ça peut être intéressant de le faire de temps en temps sur les données sensibles de votre base.

4) Supprimer définitivement les enregistrements effacés

Quand vous supprimez vos données elles ne sont pas réellement supprimés elles sont juste flaguées
Pour les supprimer vous devez faire un RGZPFM de votre table qui supprimera réellement les enregistrements et qui reconstruira les indexes .
Commencer par le faire sur les tables qui ont le plus d’enregistrements supprimés

5) Contrôler qu’il ne vous manque pas d’index

Un des principaux axe pour améliorer les performances de votre base de données est souvent d’ajouter des index manquants.

Pour connaitre les index à analyser, vous avez une table qui s’appelle QSYS2.SYSIXADV qui vous propose des suggestions d’index

Vous pouvez ajouter les index qui sont souvent et régulièrement demandés.

Vous pouvez également voir ces suggestions sur ACS.