Link to home
Start Free TrialLog in
Avatar of da-vinci
da-vinci

asked on

password protection for paradox program

Hi,

I am writing a program in delphi 7 with BDE and paradox.
Now I want some sort of protection, so you have to log on in the program (optionally).

Maybe it is also necessary to encrypt the database then. I read about a master password on the paradox table but then you have to type it in for every connection, at least that is what the documentation said.

Who has an idea for this ? Preferrably I would solve it programmatically.
Avatar of mfhobbs
mfhobbs

Hi, to avoid having to continually re-enter your table password, use:

Session.AddPassword('mypassword');

which add the password to your session object, and delphi knows to try the session's passwords first on each table access and only asks the user if no passwords match.

As to actually protect a table, see the following procedure:

procedure ProtectTable(tblname: string; tbl: TTableBDE);
{feed in tbl or, if TTable not lying around, just tblname}
var
  created: boolean;
begin
  if (tbl = nil) then
    begin
      tbl := TTableBDE.Create(nil);
      tbl.DatabaseName := ...;
      tbl.TableName := tblname;
      created := True;
    end
  else
    created := False;
  tbl.Open;
  tbl.SetMasterPassword(...masterpasswordtouse...);
  tbl.AuxiliaryPassword(...masterpwd..., ...readonlypwd... ,True,True);
  tbl.Close;
  if created then tbl.Free;
end;


The hard work is in the TableBDE class (a TTable descendant) which you can get online (by Steve Tiexera I think - sp?!).  Anyway, I fixed a bug in it and added some password logic but the important methods for you are below (too big to post the whole thing here I guess)...

Cheers,
-Mat

procedure TTableBDE.SetMasterPassword(newmaster: string);
var
  TType: array[0..40] of char;
  { Specific information about the table structure, indexes, etc. }
  TblDesc: CRTblDesc;
  { Uses as a handle to the database }
  hDb: hDbiDb;
  { Path to the currently opened table }
  TablePath: array[0..dbiMaxPathLen] of char;
  //z: DBItblname;
  //y: array[0..100] of Char;
  //p: PChar;
  //i: integer;
  //s: string;
begin
  { The table must be opened to get directory information about the table
    before closing }
  if Active <> True then
    raise EPwdError.Create('Table must be opened to have its password changed');
  { Get the type of table }
  strcopy(TType, GetTableType);
  if strcomp(TType, szParadox) <> 0 then
    begin
      raise EPwdError.Create('Can only change a password for a Paradox Table');
      exit;
    end;
  { do only if PARADOX table }

{ Initialize the table descriptor }
FillChar(TblDesc, SizeOf(CRTblDesc), 0);
with TblDesc do
begin
  { Place the table name in descriptor }
  StrPCopy(szTblName, TableName);
  { Place the table type in descriptor }
  StrCopy(szTblType, GetTableType);
  { set new password }
  bProtected := True;
  StrPCopy(szPassword,newmaster);
  bPack := False;
end;
{ Initialize the DB handle }
hDb := nil;
{ Get the current table's directory.  This is why the table MUST be
  opened until now }
Chk(DbiGetDirectory(DBHandle, True, TablePath));
{ Close the table }
Close;
{ NOW: since the DbiDoRestructure call needs a valid DB handle BUT the
  table cannot be opened, call DbiOpenDatabase to get a valid handle.
  Just leaving Active = FALSE does not give you a valid handle }
Chk(DbiOpenDatabase(nil, 'STANDARD', dbiReadWrite, dbiOpenExcl, nil,
                      0, nil, nil, hDb));
{ Set the table's directory to the old directory }
Chk(DbiSetDirectory(hDb, TablePath));
Chk(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, FALSE));
{ Re-Open the table }
Chk(DbiCloseDatabase(hDb));
Open;

end;

procedure TTableBDE.AuxiliaryPassword(master,pwd: string; readonly,add: Boolean);
{add is true to add the pwd, false to drop it}
var
  TType: array[0..40] of char;
  { Specific information about the table structure, indexes, etc. }
  TblDesc: CRTblDesc;
  { Uses as a handle to the database }
  hDb: hDbiDb;
  { Path to the currently opened table }
  TablePath: array[0..dbiMaxPathLen] of char;
  //z: DBItblname;
  //y: array[0..100] of Char;
  //p: PChar;
  i: integer;
  //s: string;
  done: boolean;
begin
{  if not add then
    begin
      raise EPwdError.Create('Drop does not work in BDE yet (?!)');
      exit;
    end;
  { The table must be opened to get directory information about the table
    before closing }
  if Active <> True then
    raise EPwdError.Create('Table must be opened to have its password changed');
  { Get the type of table }
  strcopy(TType, GetTableType);
  if strcomp(TType, szParadox) <> 0 then
    begin
      raise EPwdError.Create('Can only change a password for a Paradox Table');
      exit;
    end;
  { do only if PARADOX table }

{ Initialize the table descriptor }
FillChar(TblDesc, SizeOf(CRTblDesc), 0);
with TblDesc do
begin
  { Place the table name in descriptor }
  StrPCopy(szTblName, TableName);
  { Place the table type in descriptor }
  StrCopy(szTblType, GetTableType);
  { set new password }
  bProtected := True;
  StrPCopy(szPassword, master);

  iSecRecCount := 1;
  New(pecrSecOp);
  if add then
    pecrSecOp^ := crAdd {adding a password}
  else
    pecrSecOp^ := crDrop; {dropping a password}
  New(psecDesc);
  with psecDesc^ do
  begin
    iSecNum := 1;
    if add then
      begin
        if readonly then
          begin
            eprvTable := prvREADONLY;
            for i := 0 to (DBIMAXFLDSINSEC-1) do
              aprvFld[i] := prvREADONLY;
          end
        else
          begin
            eprvTable := prvFULL;
            for i := 0 to (DBIMAXFLDSINSEC-1) do
              aprvFld[i] := prvFULL;
          end;
        iFamRights := NOFAMRIGHTS;
      end;
    StrPCopy(szPassword,pwd);
  end;
  { Set the packing option to False}
  bPack := False;
end;
{ Initialize the DB handle }
hDb := nil;
{ Get the current table's directory.  This is why the table MUST be
  opened until now }
Chk(DbiGetDirectory(DBHandle, True, TablePath));
{ Close the table }
Close;
{ NOW: since the DbiDoRestructure call needs a valid DB handle BUT the
  table cannot be opened, call DbiOpenDatabase to get a valid handle.
  Just leaving Active = FALSE does not give you a valid handle }
Chk(DbiOpenDatabase(nil, 'STANDARD', dbiReadWrite, dbiOpenExcl, nil,
                      0, nil, nil, hDb));
{ Set the table's directory to the old directory }
Chk(DbiSetDirectory(hDb, TablePath));
{ Add Auxiliary Password }
done := False;
while not done do
begin
{  try}
    Chk(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, FALSE));
    done := True;
{  except
{    inc(TblDesc.psecDesc^.eprvTable);}
{  end;}
end;
{ Re-Open the table }
Chk(DbiCloseDatabase(hDb));
Open;

end;
Avatar of da-vinci

ASKER

What is the link to that TTable descendant ?

I like this way of providing security, but do u know how safe it is...in other words, what does one has to do to crack this master password ?
The AddPassword  method provides an optional way for an application to provide a password for a session prior to opening an encrypted Paradox or dBASE table that requires a password for access. If you do not add the password to the session, when your application attempts to open a password-protected table, a dialog box prompts the user for a password.

AddPassword takes one parameter, a string containing the password to use. You can call AddPassword as many times as necessary to add passwords (one at a time) to access tables protected with different passwords.

var
  Passwrd: String;
begin
  Passwrd := InputBox('Enter password', 'Password:', '');
  Session.AddPassword(Passwrd);
  try
    Table1.Open;
  except
    ShowMessage('Could not open table!');
    Application.Terminate;
  end;
end;

Note

Use of the InputBox function, above, is for demonstration purposes. In a real-world application, use password entry facilities that mask the password as it is entered, such as the PasswordDialog function or a custom form.

The Add button of the PasswordDialog  function dialog has the same effect as the AddPassword method.

if PasswordDialog(Session) then
  Table1.Open
else
  ShowMessage('No password given, could not open table!');
end;

Using the RemovePassword and RemoveAllPasswords methods

RemovePassword  deletes a previously added password from memory. RemovePassword takes one parameter, a string containing the password to delete.

Session.RemovePassword('secret');

RemoveAllPasswords  deletes all previously added passwords from memory.

Session.RemoveAllPasswords;

Using the GetPassword method and OnPassword event

The OnPassword  event allows you to control how your application supplies passwords for Paradox and dBASE tables when they are required. Provide a handler for the OnPassword event if you want to override the default password handling behavior. If you do not provide a handler, Delphi presents a default dialog for entering a password and no special behavior is provided--the table open attempt either succeeds or an exception is raised.

If you provide a handler for the OnPassword event, do two things in the event handler: call the AddPassword method and set the event handler's Continue parameter to True. The AddPassword method passes a string to the session to be used as a password for the table. The Continue parameter indicates to Delphi that no further password prompting need be done for this table open attempt. The default value for Continue is False, and so requires explicitly setting it to True. If Continue is False after the event handler has finished executing, an OnPassword event fires again--even if a valid password has been passed using AddPassword. If Continue is True after execution of the event handler and the string passed with AddPassword is not the valid password, the table open attempt fails and an exception is raised.

OnPassword can be triggered by two circumstances. The first is an attempt to open a password-protected table (dBASE or Paradox) when a valid password has not already been supplied to the session. (If a valid password for that table has already been supplied, the OnPassword event does not occur.)

The other circumstance is a call to the GetPassword  method. GetPassword either generates an OnPassword event, or, if the session does not have an OnPassword event handler, displays a default password dialog. It returns True if the OnPassword event handler or default dialog added a password to the session, and False if no entry at all was made.

In the following example, the Password method is designated as the OnPassword event handler for the default session by assigning it to the global Session object's OnPassword property.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Session.OnPassword := Password;
end;

In the Password method, the InputBox function prompts the user for a password. The AddPassword method then programmatically supplies the password entered in the dialog to the session.

procedure TForm1.Password(Sender: TObject; var Continue: Boolean);
var
  Passwrd: String;
begin
  Passwrd := InputBox('Enter password', 'Password:', '');
  Continue := (Passwrd > '');
  Session.AddPassword(Passwrd);
end;

The OnPassword event (and thus the Password event handler) is triggered by an attempt to open a password-protected table, as demonstrated below. Even though the user is prompted for a password in the handler for the OnPassword event, the table open attempt can still fail if they enter an invalid password or something else goes wrong.

procedure TForm1.OpenTableBtnClick(Sender: TObject);
const
  CRLF = #13 + #10;
begin
  try
    Table1.Open;                               { this line triggers the OnPassword event }
  except
    on E:Exception do begin                             { exception if cannot open table }
      ShowMessage('Error!' + CRLF +             { display error explaining what happened }
        E.Message + CRLF +
        'Terminating application...');

      Application.Terminate;                                       { end the application }
    end;
  end;
end;
Hi,  the source file is 636 lines long, I don't know if it's bad form to post it here.  Not too big I guess.  I can email it if you like, or just go ahead and post it (maybe there is a limit on a post?).  I couldn't find a link to it (its quite old, but then, so is paradox).

Re is the paradox password totally secure?  Answer: I'm afraid not,  I think there are password cracking programs out there.  I've never used one.  I wouldn't rely on it except as a mechanism to control access for regular users.  You can encrypt the data that goes into the fields - but that's a whole new issue.

Cheers,
-Mat
I have found a nice component. It's in the RxLib set of components. It's nice to use, but the disadvantage is that you have to use a new table.. users.db or sth like that. Advantage of that is providing multiple users instead of multiple passwords, like if you set the paradox table password.
But in this case you indeed want some sort of encryption for the tables. Anyone knows a good way to do that ?
ASKER CERTIFIED SOLUTION
Avatar of mfhobbs
mfhobbs

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for your good help, I got everything working now.