?
Solved

password protection for paradox program

Posted on 2003-03-13
7
Medium Priority
?
761 Views
Last Modified: 2010-04-04
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.
0
Comment
Question by:da-vinci
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
7 Comments
 

Expert Comment

by:mfhobbs
ID: 8134200
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;
0
 

Author Comment

by:da-vinci
ID: 8138284
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 ?
0
 
LVL 3

Expert Comment

by:ILE
ID: 8140855
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;
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Expert Comment

by:mfhobbs
ID: 8141120
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
0
 

Author Comment

by:da-vinci
ID: 8142041
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 ?
0
 

Accepted Solution

by:
mfhobbs earned 200 total points
ID: 8143184
Yo, the safest is to use RSA type algorithms (public/private key) - but this is only useful if you are encrypting on one machine and decrypting on another - otherwise a 'passworded' scrambling type algorithm is just as effective, and simpler to code.

See:
http://www.scramdisk.clara.net/
for some algorithms, I use a Cast128.  They are quite easy to use.

Use this to scramble values to put into textual columns.  This way does not encrypt the xx.db file itself, just its individual field values, if you know what I mean.

The trick now is to figure out how to read the data - I guess override the GetText/SetText events of your TFields - hook them all to a common GetText and a common SetText event which scrambles/unscrambles.

NB#1: If 'funny characters' are a problem in paradox (I don't know), you can uuencode to get normal characters - but increase the field size accordingly.

NB#2: No scrambling mechanism is ever 100% safe - after all your scrambling and unscrambling algorithms are sitting somewhere in the exe - which you are deploying...

NB#3: To implement a username/pwd mechanism you will need a separate 'users.db' of some kind.  The actual username or password the users use is not the paradox password, this is internal, your delphi code uses the pdox pwd to connect to the users.db table (you can have a special pwd for this table) and verify the user's username & pwd from what is stored there, and if ok, add a proper password to the session and begin.  NB: If you are using the scrambling way, forget about paradox passwords altogether - it will add no value to you.

0
 

Author Comment

by:da-vinci
ID: 8169571
Thanks for your good help, I got everything working now.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
In this video, Percona Solution Engineer Rick Golba discuss how (and why) you implement high availability in a database environment. To discuss how Percona Consulting can help with your design and architecture needs for your database and infrastr…
Suggested Courses

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question