Password Files

Many embedded systems require access control. Trevor implements the MD5 message-digest algorithm to implement one-way encryption of passwords.


January 01, 1996
URL:http://www.drdobbs.com/embedded-systems/password-files/184409809

JAN96: Password Files

Trevor is a software engineer specializing in real-time embedded systems. He can be contacted at 38 Somerset Road, Kensington, 2094, South Africa or at [email protected].


Recently, while developing an access-control method for an embedded system, I implemented one-way encryption of passwords using the MD5 Message Digest algorithm. The MD5 Message Digest Algorithm was invented by Ron Rivest of RSA Data Security (inventors of the patented RSA public-key cryptography algorithm). The MD5 algorithm calculates a practically unique 128-bit message digest of a file. It is claimed that the odds of a different file producing the same digest are quite small--one in 264. Thus, the digest protects a file, because any alteration will show up as a changed digest. The digest can be used for one-way encryption so that data can be verified without being disclosed. This is how the passwords in the method described here are protected.

The approach I implemented uses a password file that contains the user records in plaintext, together with the user digests. The entire file is protected using a digest to detect unauthorized alterations to the file. Under UNIX, password files can be read by users, but are protected from tampering by file-access restrictions. Since this wasn't possible in my embedded system, I used a file digest to protect the password file from tampering.

Each user record contains the user's name and other personal details, as well as the record's access rights and digest. The digest is calculated on the visible contents of the record as well as the user's password, to produce a unique, 128-bit key. The length of the key (and the way it is calculated) makes it unlikely that the password can be guessed.

The Password File

The password file (an ASCII file that can be read with a text editor) contains a header and a number of user entries. The header contains the file digest, a string containing the machine name, and the number of password entries in the file. The digest is calculated on the whole file so that if the file is altered in any way, this will be detected. If the digest is incorrect, an attempt may have been made to alter the file, and the file is rejected.

Each entry provides for one user. The plaintext part contains the user's name, employee number, and access rights. The digest is constructed using the plaintext part and the user-entered password. The resulting 128-bit key is stored as a string of 32 hex characters at the end of the user record. To be validated, the user supplies a username and password. The username is used to locate the user record in the password file. The plaintext part of the record, together with the user-supplied password, is used to calculate the digest. If the calculated digest is the same as the digest stored in the record, the user is validated. The user's password remains secret: It is not stored separately in the file, and it is computationally infeasible to guess it.

Because the MD5 algorithm is well known, it would be simple to change the file and recalculate the digest. One way to prevent this is to implement a keyed hash function. My approach, however, was to add encryption and scrambling to the digest; this is a key feature of my password file. There are a number of ways to scramble the digest. Any change in the order of the data will affect the digest, so even simply reordering the file as the digest is calculated would complicate things for a cracker. As long as this encryption remains secret, the password file will be impregnable.

The Code

The code that implements the technique I used consists of the password module, a command-line-driven test module, and the MD5 digest-algorithm code. The code is written for ANSI C and has been compiled and tested using the GNU C compiler (gcc) on a Sun under Solaris 1.1 (4.3 BSD) and Solaris 2.3 (basically UNIX System V), and under Linux. I obtained the MD5 digest package via FTP from info.cert.org in /pub/tools/md5 and used it as is. (Thanks to Ron Rivest and RSA Data Security for placing this code into the public domain.)

The password module consists of password.c, password.h (the public interfaces), and passwrdc.h (the public data definitions). These files, along with the MD5 modules used (as distributed by RSA Data Security), are available electronically; see "Availability," page 3. Listing One is passtest.c, a program that tests the operation of the password package. The code can be compiled and linked with gcc as follows:

gcc -O -g -Wall -static -I. -I/usr/5include -I/usr/include -o passtest passtest.c Password.c md5c.c -L/usr/5lib.

The -g flag includes debug information; this should be omitted from the final version. Figure 1 is sample output from the test program.

Running the resulting executable allows the various functions in password.c to be exercised by entering the first letter out of the list of options provided. You could start off by making a new password file and answering the questions. The resulting password file contains 20 entries--the first is a user with supervisor rights, as per the questions you answered. The remaining 19 entries are marked as unused. The single user needs supervisor rights to add and delete other users.

Once you have a valid password file, you test whether or not it is okay by pressing "I" (initialize), whereupon the file is read into memory and validated by checking the digest. Once a password file has been validated, you can perform other actions: validate, add and delete users, or change passwords. Each time a user is added/deleted, or the user changes his or her password, the password file is updated and written with the new digest. You can also display the password file in memory and generate a list of users suitable for display to a user.

The code enforces several common-sense rules:

Security

To keep the system secure, the facility to create a new password file from scratch must be guarded. Depending on the actual implementation, there are a number of ways to attack a system using the code I present here. Among the strategies an attacker could adopt are:

For More Information

RFC1321: The MD5 Message-Digest Algorithm. Ron Rivest, RSA Data Security Inc. April 1992. Available from info.cert.org in /pub/tools/md5.

Schneier, Bruce. "One-Way Hash Functions." Dr. Dobb's Journal (September 1991).

Stallings, William. "SHA: The Secure Hash Algorithm." Dr. Dobb's Journal (April 1994).

Figure 1: A starting password file with one entry with supervisor privileges. This was created using the New function in the test software. One user is defined and has supervisor privileges (logon username Trevor, password Pope). Other user records are marked as unused.

efa00b25cc4b3fb5aa29d507f9d2ad38 1
MachineName
Trevor  0123456789  s   0c47cdd73a4e8f70c67671f968eae636
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #
Unused  Nobody  *   #

Listing One

/*************************************************************************/ 
/* File: passtest.c                              */ 
/* Password package - test program                       */ 
/* Simple test program to check the operation of the password package    */ 
/* under unix.                               */ 
/* Copyright (C) 1994-5, Trevor J. Pope -- All rights reserved.      */ 
/* License to copy and use this software is granted provided that this   */ 
/* copyright notice is retained in any copies of any part of this    */ 
/* software.                                 */ 
/* No representations are made concerning either the merchantability     */ 
/* of this software or the suitability of this software for any          */ 
/* particular purpose. It is provided "as is" without express or     */ 
/* implied warranty of any kind.                     */ 
/*************************************************************************/ 
 
/* Common library includes */ 
#include <stdio.h>  /* for file I/O */ 
#include <time.h>   /* for time functions */ 
#include <string.h> /* for strcpy */ 
#include <errno.h>  /* for errno etc */ 
 
/* Pull in the standard include for the package */ 
#include "Password.h" 
 
/*************************************************************************/ 
/* main() -- Entry point for the test program                */ 
/* Passed  : command line parameter list                 */ 
/*************************************************************************/ 
int main( int argc, char * argv[], char * env[] ) 
{ 
   int result; 
   char option, optionString[80]; 
   char userName[USER_NAME_LENGTH+1]; 
   char userNumber[EMPLOYEE_NUMBER_LENGTH+1]; 
   char accessString[ACCESS_RIGHTS_LENGTH+1]; 
   char password[PASSWORD_LENGTH+1]; 
   char superUserName[USER_NAME_LENGTH+1]; 
   char superPassword[PASSWORD_LENGTH+1]; 
   char newPassword[PASSWORD_LENGTH+1]; 
   char passwordFileName[51]; 
   char machineName[MACHINE_NAME_LENGTH+1]; 
   char userDetails[USER_DETAILS_LENGTH]; 
 
   /* Loop getting options and executing them until a quit is selected */ 
   do { 
      /* Show menu */ 
      printf("\n\ 
Select:  (i)nitialise    (v)alidate (c)hange  (a)add   (d)elete\n\ 
         (n)ew  (u)sers  (s)how     (h)elp    (q)uit   : "); 
     scanf( "%s", &optionString[0] ); 
     option = optionString[0]; 
     /* Act on option ... */ 
     switch( (int)option ) 
     { 
     case 'i': 
     { 
       printf("\n initPassword - enter password file name :"); 
       scanf("%s", passwordFileName ); 
       result = initPassword( passwordFileName ); 
       printf("\n initPassword() returned %s \n", 
       &passReturnCodeStrings[result][0] ); 
       break; 
     } 
     case 'v': 
     { 
     printf("\n validateUser - enter userName :"); 
     scanf("%s", userName ); 
     printf("\n validateUser - enter pass word :"); 
     scanf("%s", password ); 
     result = validateUser( userName, password, accessString ); 
     if( result == PASS_OK ) 
     { 
          printf("validateUser() returned %s (Access level is %s )\n", 
          &passReturnCodeStrings[result][0], accessString ); 
      } else { 
          printf("validateUser() returned %s \n", 
            &passReturnCodeStrings[result][0] ); 
      } 
      break; 
    } 
    case 'c': 
    { 
       printf("\n changePassword - enter userName :"); 
       scanf("%s", userName ); 
       printf("\n changePassword - enter old pass word :"); 
       scanf("%s", password ); 
       printf("\n changePassword - enter new pass word :"); 
       scanf("%s", newPassword ); 
       result = changePassword( userName, password, newPassword ); 
       printf("changePassword() returned %s \n", 
             &passReturnCodeStrings[result][0] ); 
       break; 
      } 
      case 'a': 
      { 
      printf("\n addUser - enter supervisor userName :"); 
      scanf("%s", superUserName ); 
      printf("\n addUser - enter supervisor password :"); 
      scanf("%s", superPassword ); 
      printf("\n addUser - enter new userName :"); 
      scanf("%s", userName ); 
      printf("\n addUser - enter new user number:"); 
      scanf("%s", userNumber ); 
      printf("\n addUser - enter new user rights :"); 
      scanf("%s", accessString ); 
      printf("\n addUser - enter new user password :"); 
      scanf("%s", password ); 
      result = addUser( superUserName, superPassword, 
                  userName, userNumber, accessString, password ); 
      printf("addUser() returned %s \n", 
        &passReturnCodeStrings[result][0] ); 
      break; 
      } 
      case 'd': 
      { 
     printf("\n deleteUser - enter supervisor userName :"); 
     scanf("%s", superUserName ); 
     printf("\n deleteUser - enter supervisor password :"); 
     scanf("%s", superPassword ); 
     printf("\n addUser - enter userName to delete :"); 
     scanf("%s", userName ); 
     result = deleteUser( superUserName, superPassword, userName ); 
 
     printf("deleteUser() returned %s \n", 
        &passReturnCodeStrings[result][0] ); 
     break; 
      } 
      case 'n': 
      { 
    printf("\n newPasswordFile - enter full pathname : "); 
    scanf("%s", passwordFileName ); 
    printf("\n newPasswordFile - enter machine name :"); 
    scanf("%s", machineName ); 
    printf("\n newPasswordFile - enter supervisor name :"); 
    scanf("%s", userName ); 
    printf("\n newPasswordFile - enter supervisor number:"); 
    scanf("%s", userNumber ); 
    printf("\n newPasswordFile - enter supervisor password :"); 
    scanf("%s", password ); 
    result = newPasswordFile( passwordFileName, machineName, 
                  userName, userNumber, password ); 
    printf("newPasswordFile() returned %s \n", 
              &passReturnCodeStrings[result][0] ); 
    break; 
      } 
      case 'u': 
      { 
    showUserDetails( userDetails ); 
    printf("showUserDetails() :\n%s", userDetails ); 
 
    /* Check current user details as recorded internally */ 
    result = currentUserDetails( userName, userNumber, accessString ); 
    if( result == PASS_OK ) 
    { 
        printf("currentUserDetails() returned a valid user:\n\ 
userName: %s\n\ 
userNumber: %s\n\ 
accessString: %s\n", userName, userNumber, accessString ); 
    } else { 
         printf("currentUserDetails() returned NO valid user:\n"); 
    } 
    break; 
      } 
      case 'h': 
      { 
    printf("\n\n%s\n\n", passwordHelp() ); 
    break; 
      } 
      case 's': 
      { 
      displayPasswordStruct(); 
      break; 
      } 
      case 'q': 
      { 
      printf("\n quitting ... \n"); 
      break; 
      } 
      default: 
      { 
      printf("\n unrecognised option (%x)", (int)option ); 
      break; 
      } 
      } /* switch */ 
 
    } while( option != 'q' ); 
     return( 0 ); 
} /

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.