[Pcsclite-cvs-commit] CVS MusclePAM

CVS User rousseau ludovic.rousseau@free.fr
Wed, 15 Sep 2004 01:00:58 -0600


Update of /cvsroot/muscleapps/MusclePAM
In directory haydn:/tmp/cvs-serv18609

Modified Files:
	README certutils.c certutils.h pam_smartcard.c 
Log Message:
patch from Bruce Barnett
http://archives.neohapsis.com/archives/dev/muscle/2004-q3/0372.html


--- /cvsroot/muscleapps/MusclePAM/README	2003/05/20 21:49:50	1.4
+++ /cvsroot/muscleapps/MusclePAM/README	2004/09/15 07:00:58	1.5
@@ -36,13 +36,89 @@
 
 Configuration
 
-There is a configuration file located in /etc/pamsmartcrc. This will have
-paramaters that are read by the authentication module. Most of these are
-self-explanatory, except AuthMode. This paramater can have two values:
-1. UserCert - 	the module will look in ~/.muscle/user.cert for the 
-		certificate.
-2. RootCert - 	the module will retreive the certificate from the smartcard 
-		and validate the signature by looking at the RootCA's
-		certificate in /etc/root.cert. It will also check that the
-		username corresponds to the username in the certificate. 
-		
+There is a configuration file located in /etc/musclepam/pam-muscle.conf.
+This will have parameters that are read by the authentication module.
+Most of these are self-explanatory, except AuthMode. This parameter can
+have two values:
+
+1. UserCert -   the module will look in ~/.muscle/user.cert for the 
+                certificate. 
+2. RootCert -   the module will retreive the certificate from the smartcard 
+                and validate the signature by looking at the RootCA's
+                certificate in /etc/root.cert. It will also check that the
+                username corresponds to the username in the certificate. 
+                
+
+
+If UserCert is selected, then the certificate can be in one of three formats.
+    1) DER/BER X509 - this is a binary format
+    2) PEM X509 (PEM_read_X509) - in PEM format
+
+    This is in base64 format, and has the form
+        -----BEGIN CERTIFICATE-----
+        MIIEzzCCA7egAwIBAgIBADANBgkqhkiG9w0BAQQFADBvMQswCQYDVQQGEwJkZTEg
+        [snip]
+        -----END CERTIFICATE-----
+    3) PEM - PUBKEY
+        -----BEGIN PUBLIC KEY-----
+        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhs17bA92YPg8XrR9s34qoK2Fc
+        [snip]
+        -----END PUBLIC KEY-----
+
+    The third form can be obtained by using musacletool to create a
+    keypair, export it, and then using b2fs to convert it into a
+    pubkey format
+
+    Example
+
+    $ muscleTool
+    muscle > tokens
+       1.    MuscleCard Applet
+    muscle > connect 1
+    muscle [MuscleCard Applet] > verify 1 <PINVALUE>
+    PIN Verify Successful
+    muscle [MuscleCard Applet] > genkeys
+    ***************** Key Generation Routine *****************
+    Note: Keys will be generated with default permissions
+               using the RSA CRT algorithm for generation.
+    **********************************************************
+    Enter the private key number: 1
+    Enter the public key number: 2
+    Are you sure ? (Y/N): y
+    Generating keys 
+    *******************************] : Success
+    muscle [MuscleCard Applet] > listkeys
+                Key Type      Key Num   SIZE     READ   WRITE     USE
+       -----------------  -----------   -----   ------  ------  ------
+         RSA PRIVATE CRT            1   1024     NEVER  PIN #1  PIN #1
+              RSA PUBLIC            2   1024    ALWAYS  PIN #1  PIN #1
+    muscle [MuscleCard Applet] > exportkey 2 cert.blob
+    Export Key Successful
+    muscle [MuscleCard Applet] > release
+    muscle > exit
+
+    # now use b2fs to convert the blob into a PEM public key certificate
+    % b2fs cert.blob >~/.muscle/user.cert
+
+
+Note that the user can do this without any outside authority. Make sure
+the home directory and certificate is not writable. (pam_musclecard.so
+should check to make sure this is not possible. However it doesn't, so
+this is a potential security problem).
+
+Once you have the certificate in place, I have been able to get the su
+and login modules to work with PAM.
+
+Modify
+    /etc/pam.d/su
+to contain
+   auth       required     /lib/security/pam_musclecard.so service=system-auth
+
+ and /etc/pam.d/login to contain
+   auth       required /lib/security/pam_musclecard.so service=system-auth
+
+and when you try to log into the console, or to do an su, it first
+checks if a smartcard is plugged in. If so, it asks for a PIN value.
+Then it procees to verify it with the user's ~user/.muscle/user.cert
+file.
+
--- /cvsroot/muscleapps/MusclePAM/certutils.c	2003/05/20 21:46:58	1.5
+++ /cvsroot/muscleapps/MusclePAM/certutils.c	2004/09/15 07:00:58	1.6
@@ -76,22 +76,49 @@
  
   // Open file
   fp = fopen(file, "rb");
-  if (fp == NULL)
+  if (fp == NULL) {
+    syslog(LOG_ERR, "cannot open certificate file %s: %s", file, strerror(errno));
     return -1;
- 
+  }
   // Get the cert
   PEM_read_X509(fp,&mycert,NULL,NULL);
 
   fclose(fp);
 
-  if (mycert == NULL)
+  if (mycert == NULL) {
+    syslog(LOG_ERR, "File %s does not contain X509 certificate - PEM_read_X509() fails", file);
     return -1;
- 
+  }
+
   *cert = mycert;
  
   return 0;
 }
 
+/* add the code to read a public key certificate from a file */
+int getPubKeyFromFile(const char *file, EVP_PKEY **cert) {
+  FILE *fp;
+  EVP_PKEY *mycert = NULL;
+ 
+  // Open file
+  fp = fopen(file, "rb");
+  if (fp == NULL) {
+    syslog(LOG_ERR, "cannot open certificate file %s: %s", file, strerror(errno));
+    return -1;
+  }
+  // Get the cert
+  PEM_read_PUBKEY(fp,&mycert,NULL,NULL);
+
+  fclose(fp);
+
+  if (mycert == NULL) {
+    syslog(LOG_ERR, "File %s does not contain Public Key certificate - PEM_read_PUBKEY() fails", file);
+    return -1;
+  }
+  *cert = mycert; 
+  return 0;
+}
+
 /* Method to read a certificate from a a smartcard */
 int getCardCert(MSCTokenConnection pConnection, X509 **cert) {
   int rv;
--- /cvsroot/muscleapps/MusclePAM/certutils.h	2003/10/03 19:30:31	1.4
+++ /cvsroot/muscleapps/MusclePAM/certutils.h	2004/09/15 07:00:58	1.5
@@ -66,6 +66,14 @@
  */
 int getPublicKey(X509 *cert, EVP_PKEY** pub_key);
 
+/**
+ * Extract the public key of a given certificate from a file
+ *
+ * @param filename    - certificate that stores the public key
+ * @param pub_key - public key object to write key into
+ * @returns 0 on success, -1 otherwise
+ */
+int getPubKeyFromFile(const char *filename, EVP_PKEY** pub_key);
 #endif
 
 /*** end of file ***********************************************************/
--- /cvsroot/muscleapps/MusclePAM/pam_smartcard.c	2003/10/02 19:30:13	1.15
+++ /cvsroot/muscleapps/MusclePAM/pam_smartcard.c	2004/09/15 07:00:58	1.16
@@ -8,6 +8,7 @@
  *   Mario Strasser       <mast@gmx.net>                            *
  *   David Corcoran       <corcoran@linuxnet.com>                   *
  *   Eirik A. Herskedal   <ehersked@cs.purdue.edu>                  *
+ *   Bruce Barnett        <muscle040302@grymoire.com>
  *                                                                  *
  ********************************************************************
  *                                                                  *
@@ -250,37 +251,47 @@
   return 0;
 }
 
-int readUserCert(X509 **userCert, MSCTokenConnection pConnection, 
+int readUserPubKey(EVP_PKEY **userCert, MSCTokenConnection pConnection, 
 		 struct secure_data *sd) {
   int rv;
-  X509 *tmpCert;
+  EVP_PKEY *tmpCert;
+  X509 *tmpCert1;
   unsigned char homeFile[200];
+  /* tries to get a public key information from the user's public key certificate */
 
   snprintf(homeFile, 200, "%s%s/.muscle/%s", pr.userpath, sd->user, pr.certname);  
 
-  rv = getFileCert(homeFile, &tmpCert);
+  rv = getFileCert(homeFile, &tmpCert1);
 
   if (rv == -1) {
-    /* Try to read a PEM cert */
-    rv = getFileCertPEM(homeFile, &tmpCert);
-  }
+    /* Didn't work - now try public key cert */
+    /* for UserAuth - this could also be a public key Certificate, not an X509 (signed) */
 
-  if (rv == -1) {
-    syslog(LOG_ERR, "cannot read certificate from %s", homeFile);
-    pcsc_release(&pConnection);
-    pam_release_data(sd);
-    return PAM_AUTHINFO_UNAVAIL;
-  }
+	/* This makes the assumption the user created the certificate
+	   without an outside authority to sign it. */
 
-  rv = checkCert(tmpCert);
+    rv = getPubKeyFromFile(homeFile, &tmpCert);
+  } else {
+	syslog(LOG_ERR, "user certificate successfully read from %s", homeFile);
+	rv = checkCert(tmpCert1);
 
+	if (rv == -1) {
+	  syslog(LOG_ERR, "user certificate expired or revoked");
+	  pcsc_release(&pConnection);
+	  pam_release_data(sd);
+	  return PAM_AUTHINFO_UNAVAIL;
+	}
+	rv = getPublicKey(tmpCert1, &tmpCert);
+  }	
+  
   if (rv == -1) {
-    syslog(LOG_ERR, "user certificate expired or revoked");
+    syslog(LOG_ERR, "cannot read certificate from %s", homeFile);
     pcsc_release(&pConnection);
     pam_release_data(sd);
     return PAM_AUTHINFO_UNAVAIL;
   }
 
+
   *userCert = tmpCert;
 
   return 0;
@@ -311,7 +322,7 @@
   util_ReadPreferences();
 
   /* open log */
-  //openlog(LOGNAME, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+  openlog(LOGNAME, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
 
   /* init data */
   sd = (struct secure_data*)malloc(sizeof(struct secure_data));
@@ -323,12 +334,12 @@
   /* init pcsc */
   rv = pcsc_init(&pConnection, reader);
   if (rv != MSC_SUCCESS) {
-    syslog(LOG_ERR, "musclecard error: %s", msc_error(rv));
+    syslog(LOG_ERR, "musclecard error during pcsc_init: %s", msc_error(rv));
     pam_release_data(sd);
     return PAM_AUTHINFO_UNAVAIL;
   }
 
-  if (pr.debug) printf("Welcome\n");
+  if (pr.debug) printf("Welcome to pam_musclecard.so verification Module\n");
 
   /* get user name */
   rv = pam_get_user(pamh, &sd->user, NULL);
@@ -339,6 +350,7 @@
     return PAM_USER_UNKNOWN;
   }
 
+  if (pr.debug) printf("User = %s\n", sd->user);
   /* get password */
   if (use_first_pass) {
     rv = pam_get_pin(pamh, sd->pin, NULL, PAM_AUTHTOK, 0);
@@ -356,8 +368,7 @@
     return PAM_AUTHINFO_UNAVAIL;
   }
 
-  if (pr.debug)
-    printf("pin  = %s\n\n", sd->pin);
+  /*  if (pr.debug) printf("pin  = %s\n\n", sd->pin); */
 
   /* get random value */
   rv = getRandom(sd->rand, RAND_SIZE);
@@ -377,7 +388,7 @@
     printf("\n\n");
   }
 
-  /* We must get the ACL for key #3 to determine which pin must
+  /* We must get the ACL for key #3 (or whatever) to determine which pin must
      be verified                                                */
 
   /* verify chv1 */
@@ -385,8 +396,8 @@
   rv = MSCVerifyPIN(&pConnection, pr.pinnumber, sd->pin, PIN_SIZE);
   
   if (rv != MSC_SUCCESS) {
-    printf("Invalid PIN Entered\n");
-    syslog(LOG_ERR, "musclecard error: %s", msc_error(rv));
+    if (pr.debug) printf("Invalid PIN Entered\n");
+    syslog(LOG_ERR, "musclecard error during Verify PIN: %s", msc_error(rv));
     pcsc_release(&pConnection);
     pam_release_data(sd);
     return PAM_AUTHINFO_UNAVAIL;
@@ -403,8 +414,13 @@
   rv = MSCComputeCrypt(&pConnection, &cryptInit, (MSCPUChar8)sd->rand,
 		       RAND_SIZE, (MSCPUChar8)sd->cipher, &cryptSize);
 
-  if (rv != MSC_SUCCESS) {
-    syslog(LOG_ERR, "musclecard error: %s", msc_error(rv));
+  if (rv == MSC_INCORRECT_P1) {
+    syslog(LOG_ERR, "musclecard error during Compute Crypt,  incorrect P1 value, certificate #%d: %s", pr.certnumber, msc_error(rv));
+    pcsc_release(&pConnection);
+    pam_release_data(sd);
+    return PAM_AUTHINFO_UNAVAIL;
+  }	else if (rv != MSC_SUCCESS) {
+    syslog(LOG_ERR, "musclecard error during Compute Crypt, certificate #%d, %s", pr.certnumber, msc_error(rv));
     pcsc_release(&pConnection);
     pam_release_data(sd);
     return PAM_AUTHINFO_UNAVAIL;
@@ -421,15 +437,14 @@
 
   /* get the certs for the user and root CA */
 
-  if (pr.authmode == ROOTCERT)
+  if (pr.authmode == ROOTCERT) {
     rv = readRootCert(&userCert, pConnection, sd);
-  else
-    rv = readUserCert(&userCert, pConnection, sd);
-
-  if (rv != 0)
-    return PAM_AUTHINFO_UNAVAIL;
-
-  rv = getPublicKey(userCert, &pubkey);
+      if (rv != 0)
+        return PAM_AUTHINFO_UNAVAIL;
+    rv = getPublicKey(userCert, &pubkey);
+  } else {
+    rv = readUserPubKey(&pubkey, pConnection, sd);
+  }
 
   if (rv == -1) {
     syslog(LOG_ERR, "cannot read public key file from user certificate");
@@ -438,6 +453,22 @@
     return PAM_AUTHINFO_UNAVAIL;
   }
 
+
+  /* This sectionj can be used to verify the user's certificate */
+#ifdef DEBUG_SPECIAL1
+  if (pr.debug) {
+	syslog(LOG_ERR, "Public key file read from user certificate");
+	printf("public exponent : %s\n", BN_bn2hex(pubkey->pkey.rsa->e));
+	printf("public modulus : %s\n", BN_bn2hex(pubkey->pkey.rsa->n));
+  }
+
+  BIO *eout=NULL;
+  eout = BIO_new(BIO_s_file());
+  BIO_set_fp(eout,stdout,BIO_NOCLOSE);
+  RSA_print(eout, pubkey->pkey.rsa, 0);
+
+#endif
+
   rv = RSA_public_decrypt(128, sd->cipher, sd->plain, pubkey->pkey.rsa, 
 			   RSA_NO_PADDING);  
 
@@ -447,7 +478,7 @@
   if (rv == -1) {
     syslog(LOG_ERR, "cannot decode random number");
     ERR_error_string(ERR_get_error(), error);
-    printf("Error from openssl:\t %s\n", error);
+    if (pr.debug) printf("Error from openssl:\t %s\n", error);
     pcsc_release(&pConnection);
     pam_release_data(sd);
     return PAM_AUTHINFO_UNAVAIL;
@@ -462,9 +493,80 @@
     printf("\n\n");
   }
 
+
+  /* Here is another debug section. It asks the card to decrypy what it sent out.
+   * This should be the same as the input.
+   * Normally this isn't used - Bruce */
+
+#ifdef DEBUG_SPECIAL
+  if (pr.debug) {
+	/* double check to make sure this works. We should be able to ask the card to decrypt this as well */
+
+/* assume the next certificate is the matching public key - This may be a bas assumption. -Bruce */
+	cryptInit.keyNum          = pr.certnumber + 1;  
+	cryptInit.cipherMode      = MSC_MODE_RSA_NOPAD;
+	cryptInit.cipherDirection = MSC_DIR_DECRYPT;
+	cryptInit.optParams       = NULL;
+	cryptInit.optParamsSize   = 0;
+	cryptSize = RAND_SIZE;
+
+	struct secure_data *sd2;
+	sd2 = (struct secure_data*)malloc(sizeof(struct secure_data));
+	if (sd2 == NULL) {
+	  syslog(LOG_CRIT, "not enough free memory to initialize sd2");
+	  return PAM_AUTHINFO_UNAVAIL;
+	}
+	memcpy(sd2->rand,sd->cipher,RAND_SIZE);
+	rv = MSCComputeCrypt(&pConnection, &cryptInit, (MSCPUChar8)sd2->rand,
+						 RAND_SIZE, (MSCPUChar8)sd2->cipher, &cryptSize);
+
+	if (rv != MSC_SUCCESS) {
+	  
+	  syslog(LOG_ERR, "musclecard error during reverse Compute Crypt, certificate #%d: %s", cryptInit.keyNum, msc_error(rv));
+	}
+	printf("Undecoded value using key #%d  = ", cryptInit.keyNum);
+	  for (i=0; i < RAND_SIZE; i++) {
+		rv = (unsigned char)sd2->cipher[i];
+		printf("%02x", rv);
+	  }	
+	  printf("\n\n");
+  }
+	printf("Compare two values size by side: \n");
+	  for (i=0; i < 128; i+=8) {
+		printf("\t%02X%02X%02X%02X %02X%02X%02X%02X == %02X%02X%02X%02X %02X%02X%02X%02X?\n",
+			   (unsigned char)sd->rand[i],
+			   (unsigned char)sd->rand[i+1],
+			   (unsigned char)sd->rand[i+2],
+			   (unsigned char)sd->rand[i+3],
+			   (unsigned char)sd->rand[i+4],
+			   (unsigned char)sd->rand[i+5],
+			   (unsigned char)sd->rand[i+6],
+			   (unsigned char)sd->rand[i+7],
+			   (unsigned char)sd->plain[i],
+			   (unsigned char)sd->plain[i+1],
+			   (unsigned char)sd->plain[i+2],
+			   (unsigned char)sd->plain[i+3],
+			   (unsigned char)sd->plain[i+4],
+			   (unsigned char)sd->plain[i+5],
+			   (unsigned char)sd->plain[i+6],
+			   (unsigned char)sd->plain[i+7]
+			   );
+	  }	
+
+#endif
+
   /* compare original value and result from smartcard */
-  if (memcmp(sd->rand, sd->plain, RAND_SIZE) == 0) result = PAM_SUCCESS;
-  else result = PAM_AUTH_ERR;
+  if (memcmp(sd->rand, sd->plain, RAND_SIZE) == 0) {
+	if (pr.debug)
+	  printf("Challenge was Successfully met\n");
+	result = PAM_SUCCESS;
+  } else {
+	if (pr.debug)
+	  syslog(LOG_ERR, "musclecard challenge failed for user %s", sd->user);
+
+	result = PAM_AUTH_ERR; 
+  }  
+  
 
   /* Release certificates */
   X509_free(userCert);