Suivez-nous sur X
|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - protégez vos données
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - juillet 1995)
|
|
Il est souvent utile de protéger ses données des regards indiscrets par un système de mots de passe. Sous Unix, c'est chose
courante dans la mesure où la plupart du temps plusieurs personnes utilisent une machine. Sur Amiga, le système n'offre pas
de telles protections par lui-même : il faut l'y aider. Le but de cet article n'est pas de vous offrir une protection absolue.
D'ailleurs, les éditeurs de jeux le savent bien, il n'y a pas de sécurité totale. Un sorcier pourra toujours se débrouiller
pour aller fouiner dans vos fichiers. Il s'agit juste de vous proposer un petit programme gérant des mots de passe pour
plusieurs utilisateurs et pouvant être utilisé dans un script. J'en profiterai pour vous présenter la bibliothèque liable
amiga.lib et la reqtools.library, que Nico François a placé en gratuiciel.
Je précise tout de suite qu'un tel système de mots de passe ne vaut que si ceux-ci sont sauvegardés sous forme codée avec les
données à protéger et que l'utilitaire chargé de leur gestion vérifie le mot de passe à chaque fois. Il peut également être utile
de coder les données à l'aide de ce mot de passe, un peu comme le fait l'algorithme de cryptage de la powerpacker.library.
Le lien (linkage)
Le coeur du programme est la fonction ACrypt(). Ne cherchez pas dans la dos.library ou intuition.library, elle ne s'y trouve pas.
Il s'agit de l'une des nouvelles fonctions de l'amiga.lib, qui est une bibliothèque liable. A la différence des bibliothèques
partagées (run-time comme disent les grands bretons), les routines que contiennent les bibliothèques liables sont explicitement
incluses dans le code exécutable. Ce sont surtout des "stubs" pour la programmation en C, mais amiga.lib contient quelques
routines intéressantes en assembleur. La plupart (pas toutes) exigent que le passage de paramètres se fasse sur la pile (au
lieu des registres), à la manière du C : on empile les paramètres (uniquement des mots longs, il ne s'agit pas de provoquer une
"adress error" quand le système décide de commuter la tâche !) par ordre inverse de leur énumération, on saute à la routine par
un JSR et surtout on n'oublie pas de corriger le pointeur de pile : on lui ajoute 4 par paramètre.
Lors de l'assemblage, il ne faut pas produire un code exécutable comme on en a l'habitude mais un code liable (avec le suffixe .o).
Pour Devpac, on réclame ce mode soit dans la fenêtre de configuration de l'assembleur, soit avec l'option ALINK sur la première
ligne du source. Les symboles à exporter (ceux que vous voulez accessibles à d'autres modules) sont à déclarer avec la directive
XDEF (eXternal DEFinition) et ceux à importer (c'est-à-dire ceux qui sont définis dans d'autres modules) par XREF (eXternal
REFerence). L'éditeur de liens (BLink pour Devpac) fait le reste en rassemblant les différents modules objets dans un exécutable et en
résolvant tous ces liens. C'est ici qu'interviennent les bibliothèques liables : ce sont des gros fichiers qui contiennent une
multitude de fonctions, dont seules celles qui sont utilisées sont réellement incluses dans l'exécutable.
Ici, la seule référence externe est le nom de la fonction : _ACrypt. Comme vous le montrera un message d'erreur de BLink si vous
l'oubliez, elle a besoin d'une définition qui est _SysBase : adresse d'une variable contenant l'adresse de la structure ExecBase.
Vous pouvez la mettre à 4. Remarquez le nom de la fonction qui commence par un underscore : c'est le cas de tous les symboles
accessibles par un compilateur C. Là où un compilateur C lit ACrypt, l'assembleur lit _ACrypt. Ce stratagème permet par exemple de
définir deux symboles pour une même fonction : un point d'entrée (sans underscore) avec passage des paramètres dans les registres
pour l'assembleur et un autre (avec underscore) avec passage des paramètres sur la pile pour le C (et les autres langages), qui
met les paramètres dans les registres et saute au premier point. On retrouve un peu le même genre de système dans le Hooks avec
les champs h_Entry et h_SubEntry.
Le cryptage
On utilise donc la fonction ACrypt(). Pour cela, on commence par demander le nom de l'utilisateur. J'ai utilisé, par souci de
concision, la fonction rtGetStringA() de la reqtools.library. Il s'agit d'une bibliothèque partagée très performante écrite par
Nico François qui rend l'affichage de fenêtres de requêtes de toutes sortes un jeu d'enfant. On la trouve absolument partout dans
les collections de domaine public.
Une fois ce nom entré, on le place dans une variable d'environnement locale (je ne m'étendrai pas sur ce sujet aujourd'hui car la
place m'est comptée) et on le crypte par ACrypt(). Cette fonction s'attend à trouver à la fois un mot de passe à crypter et le
nom de l'utilisateur (qui est utilisé pour le codage). Ici, on utilise deux fois le nom de l'utilisateur. Le résultat de cette
fonction est toujours un mot d'exactement 11 lettres et constitué de caractères imprimables, ce qui permet de l'utiliser comme
nom de fichier.
On examine ensuite le répertoire L:Passwords/ à la recherche d'un fichier portant ce nom. S'il n'existe pas, ou si le répertoire
n'existe pas, il s'agit d'un nouvel utilisateur. On lui demande alors d'entrer un nouveau mot de passe (avec confirmation et entrée
invisible, merci la reqtools.library), qu'on code et qu'on enregistre dans un nouveau fichier portant le nom de l'utilisateur codé,
et en créant le répertoire s'il n'existe pas. Si le fichier en question existe déjà, on le lit et on demande le mot de passe à
l'utilisateur. On les compare et on retourne le code correspondant (voir code source), ce qui permet un test facile par la commande
IF dans un script.
Il est à noter que la fonction ACrypt() n'est disponible qu'à partir de la version 39 de l'amiga.lib, mais fonctionne sous la
version 37 du Kickstart. Si la dépendance à la reqtools.library vous insupporte pour une raison qui ne peux imaginer, vous pouvez
toujours réécrire une routine n'utilisant qu'Intuition, mais pourquoi réinventer la roue ?
Pour le programme, vous obtenez après assemblage un fichier avec le suffixe .o (mettons Pass.o). Vous devez alors lier avec la
bibliothèque amiga.lib (on la trouve avec Devpac et dans le kit développeur 3.1) avec une ligne de commande du style :
Blink FROM Pass.o TO Pass LIB LIB:amiga.lib
|
J'oubliais : le programme est réentrant, c'est-à-dire qu'il ne gère aucune variable globale (on réserve juste une zone mémoire qui
est toujours pointée par A5 et qui contient toutes les variables et tous les tampons nécessaires. De cette façon, vous pouvez
rendre ce programme résident par la commande Resident.
Ce qu'il vous reste à écrire maintenant est un programme qui permet à un utilisateur de changer son mot de passe. Il y a bien sûr
la possibilité d'effacer son fichier du répertoire L:Passwords, mais ce n'est pas facile de savoir lequel s'il y en a cinquante.
Il faudrait de plus prévoir un système de stockage plus sûr qu'un simple fichier, comme un nouveau système de fichiers codant ses
données (un peu comme XDisk), mais c'est assez difficile. Je laisse ce problème à votre sagacité. Passez de bonnes vacances et,
rendez-vous à la rentrée pour de nouvelles aventures.
opt ALINK
; codes de retour:
; 0: OK, pas de problème
; 10: Mauvais mot de passe
; 20: Erreur
; 100: Erreur d'initialisation
include exec/exec.i
include exec/exec_lib.i
include dos/dos.i
include dos/var.i
include dos/dos_lib.i
include libraries/reqtools.i
include libraries/reqtools_lib.i
STRUCTURE MyData,0
APTR md_ExecBase
APTR md_DOSBase
APTR md_ReqToolsBase
BPTR md_PasswordHandle
STRUCT md_UserName,40
STRUCT md_CryptedName,12
STRUCT md_Password1,20
STRUCT md_Password2,20
STRUCT md_CryptedPass1,12
STRUCT md_CryptedPass2,12
LABEL md_SIZEOF
_SysBase EQU 4
XREF _ACrypt
XDEF _SysBase
moveq #100,d7 ; code de retour
move.l 4.w,a6
move.l #md_SIZEOF,d0
move.l #MEMF_PUBLIC!MEMF_CLEAR,d1
jsr _LVOAllocMem(a6)
move.l d0,a5
move.l a5,d0
beq exit
move.l a6,md_ExecBase(a5)
lea DOS.Name(pc),a1
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,md_DOSBase(a5)
beq freemem
lea ReqTools.Name(pc),a1
moveq #38,d0
jsr _LVOOpenLibrary(a6)
move.l d0,md_ReqToolsBase(a5)
beq closedos
; demande le nom de l'utilisateur
lea md_UserName(a5),a1
move.l #39,d0
lea Requester.Title(pc),a2
suba.l a3,a3
lea UserName.Tags(pc),a0
move.l md_ReqToolsBase(a5),a6
jsr _LVOrtGetStringA(a6)
move.l #User.VarName,d1
lea md_UserName(a5),a0
move.l a0,d2
move.l #-1,d3
move.l #GVF_LOCAL_ONLY,d4
move.l md_DOSBase(a5),a6
jsr _LVOSetVar(a6)
tst.w d0
beq closereqtools
pea md_UserName(a5)
pea md_UserName(a5)
pea md_CryptedName(a5)
jsr _ACrypt
add.l #12,sp
tst.l d0
beq closereqtools
bsr LockPassDir
move.l d0,d6 ; d6=lock du dir
beq.s NewPass
move.l d6,d1
jsr _LVOCurrentDir(a6)
move.l d0,d5 ; d5=ancien dir
lea md_CryptedName(a5),a0
move.l a0,d1
move.l #MODE_OLDFILE,d2
jsr _LVOOpen(a6)
move.l d0,md_PasswordHandle(a5)
bne ComparePass
move.l d5,d1
jsr _LVOCurrentDir(a6)
move.l d6,d1
jsr _LVOUnLock(a6)
; nouveau mot de passe à créer
NewPass moveq #20,d7
lea GetNewPass.MSG(pc),a0
lea md_Password1(a5),a1
bsr GetPassword
beq closereqtools
lea ConfirmPass.MSG(pc),a0
lea md_Password2(a5),a1
bsr GetPassword
beq closereqtools
; comparer les deux mots de passe:
lea md_Password1(a5),a0
lea md_Password2(a5),a1
.CompLoop
cmp.b (a0)+,(a1)+
bne closereqtools
tst.b -1(a0)
bne.s .CompLoop
bsr CryptPass
beq closereqtools
; enregistrer dans le fichier
bsr LockPassDir
move.l d0,d6
bne.s .PassDirGot
move.l #Pass.DirName,d1
jsr _LVOCreateDir(a6)
move.l d0,d6
beq closereqtools
move.l #Pass.DirName,d1
move.l #FIBF_EXECUTE!FIBF_DELETE,d2
jsr _LVOSetProtection(a6)
.PassDirGot
move.l d6,d1
jsr _LVOCurrentDir(a6)
move.l d0,d5
lea md_CryptedName(a5),a0
move.l a0,d1
move.l #MODE_NEWFILE,d2
jsr _LVOOpen(a6)
move.l d0,md_PasswordHandle(a5)
beq.s .Unlockdir
move.l d0,d1
lea md_CryptedPass1(a5),a0
move.l a0,d2
move.l #12,d3
jsr _LVOWrite(a6)
cmp.l d3,d0
bne.s .NotOK
moveq #0,d7 ; retour: OK
.NotOK move.l md_PasswordHandle(a5),d1
jsr _LVOClose(a6)
lea md_CryptedName(a5),a0
move.l a0,d1
move.l #FIBF_WRITE!FIBF_DELETE!FIBF_EXECUTE,d2
jsr _LVOSetProtection(a6)
.Unlockdir
move.l d5,d1
jsr _LVOCurrentDir(a6)
move.l d6,d1
jsr _LVOUnLock(a6)
bra.s closereqtools
ComparePass
moveq #20,d7
move.l md_PasswordHandle(a5),d1
lea md_CryptedPass2(a5),a0
move.l a0,d2
move.l #12,d3
jsr _LVORead(a6)
cmp.l d3,d0
bne.s .CloseIt
lea EnterPass.MSG(pc),a0
lea md_Password1(a5),a1
bsr.s GetPassword
beq.s .CloseIt
bsr CryptPass
lea md_CryptedPass1(a5),a0
lea md_CryptedPass2(a5),a1
move.l #2,d0 ; 3 mots longs=12 chars
.Loop cmp.l (a0)+,(a1)+
bne.s .BadPass
dbra d0,.Loop
moveq #0,d7
bra.s .CloseIt
.BadPass
moveq #10,d7
lea BadPass.MSG(pc),a1
lea BadPass.GAD(pc),a2
suba.l a3,a3
move.l a3,a0
move.l md_ReqToolsBase(a5),a6
jsr _LVOrtEZRequestA(a6)
.CloseIt
move.l md_DOSBase(a5),a6
move.l md_PasswordHandle(a5),d1
jsr _LVOClose(a6)
move.l d5,d1
jsr _LVOCurrentDir(a6)
move.l d6,d1
jsr _LVOUnLock(a6)
closereqtools
move.l md_ExecBase(a5),a6
move.l md_ReqToolsBase(a5),a1
jsr _LVOCloseLibrary(a6)
closedos
move.l md_DOSBase(a5),a1
jsr _LVOCloseLibrary(a6)
freemem move.l a5,a1
move.l #md_SIZEOF,d0
jsr _LVOFreeMem(a6)
exit move.l d7,d0
rts
GetPassword ; (MSG,Buffer)(A0,A1)
move.l md_ReqToolsBase(a5),a6
clr.l -(sp)
pea GSREQF_CENTERTEXT
pea RTGS_Flags
move.l a0,-(sp)
pea RTGS_TextFmt
pea -1
pea RTGS_Invisible
move.l sp,a0
move.l #19,d0
lea Requester.Title(pc),a2
suba.l a3,a3
jsr _LVOrtGetStringA(a6)
add.l #7*4,sp
tst.l d0
rts
LockPassDir
move.l md_DOSBase(a5),a6
move.l #Pass.DirName,d1
jsr _LVOLock(a6)
tst.l d0
rts
CryptPass
pea md_UserName(a5)
pea md_Password1(a5)
pea md_CryptedPass1(a5)
jsr _ACrypt
add.l #12,sp
rts
UserName.Tags
dc.l RTGS_Flags,GSREQF_CENTERTEXT
dc.l RTGS_TextFmt,UserName.MSG
dc.l TAG_DONE
DOS.Name dc.b 'dos.library',0
ReqTools.Name dc.b 'reqtools.library',0
Requester.Title dc.b 'Password',0
UserName.MSG dc.b 'Enter your name:',0
User.VarName dc.b 'User',0
Pass.DirName dc.b 'L:Passwords',0
GetNewPass.MSG dc.b 'Enter a new password:',0
ConfirmPass.MSG dc.b 'Please confirm:',0
EnterPass.MSG dc.b 'Enter password:',0
BadPass.MSG dc.b 'Wrong password !',10
dc.b 'See your system supervisor.',0
BadPass.GAD dc.b 'Will do.',0
|
|