This patch adds testcases for the Integrity Measurement Architecture(IMA).

Signed-off-by: Mimi Zohar <[email protected]>

Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/README
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/README
@@ -0,0 +1,40 @@
+These testcases test the Integrity Measurement Architecture(IMA).
+
+Requirements
+------------
+In order for all of the tests in the testsuite to complete successfully:
+       - A kernel with Linux Integrity Module(LIM), IMA, CRYPTO, SYSFS,
+         SECURITYFS, TPM support and TPM driver builtin is required.
+       - The testsuite must be executed with root priveleges so that it
+         can access securityfs files, such as: security/ima/policy and
+         security/ima/ascii_runtime_measurement.
+       - Both sysfs and securityfs if not mounted, will be mounted.
+       - To verify the IMA measurement list, openssl must be installed.
+         If it is not installed, these tests will not be compiled or executed.
+       - To test that on the next read after a file changes, the file will
+         be re-measured, the $TMP directory must be mounted with i_version
+         support(Bugzilla 471593). If $TMP is on any partition other than
+         root, add 'iversion' option in /etc/fstab: "defaults,iversion".
+         If $TMP is on the root partition, 'iversion' must be added in
+         /etc/rc.sysinit:
+
+       # Remount the root filesystem read-write.
+       update_boot_stage RCmountfs
+       if remount_needed ; then
+         action $"Remounting root filesystem in read-write mode: " mount -n -o 
remount,rw,iversion /
+       fi
+
+
+Dependency
+----------
+The testsuite is dependent on the default policy being enabled, which
+measures all executables, all files mmapped for execute and all files
+open for read by root. If the default policy has been replaced, loading
+another measurement policy will fail, as the policy may only be replaced
+once per boot. Some of the policy dependency tests might also fail as well.
+
+System State after running the Testsuite
+----------------------------------------
+After running the testsuite, the default measurement policy was replaced
+with an identical policy.  If you want to install a different one, such
+as an LSM specific one, a reboot would be required.
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/measure.policy
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/measure.policy
@@ -0,0 +1,16 @@
+#
+# Integrity measure policy
+#
+# PROC_SUPER_MAGIC
+dont_measure fsmagic=0x9fa0
+# SYSFS_MAGIC
+dont_measure fsmagic=0x62656572
+# DEBUGFS_MAGIC
+dont_measure fsmagic=0x64626720
+# TMPFS_MAGIC
+dont_measure fsmagic=0x01021994
+# SECURITYFS_MAGIC
+dont_measure fsmagic=0x73636673
+measure func=FILE_MMAP mask=MAY_EXEC
+measure func=BPRM_CHECK mask=MAY_EXEC
+measure func=PATH_CHECK mask=MAY_READ uid=0
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/measure.policy-invalid
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/measure.policy-invalid
@@ -0,0 +1,16 @@
+#
+# Integrity measure policy
+#
+# PROC_SUPER_MAGIC
+dont_measure fsmagic=0x9fa0
+# SYSFS_MAGIC
+dont_measure fsmagic=0x62656572
+# DEBUGFS_MAGIC
+dont_measure fsmagic=0x64626720
+# TMPFS_MAGIC
+dont_measure fsmagic=0x01021994
+# SECURITYFS_MAGIC
+dnt_measure fsmagic=0x73636673
+measure func=FILE_MMAP mask=MAY_EXEC
+measure func=BPRM_CHECK mask=MAY_EXEC
+measure func=PATH_CHECK mask=MAY_READ uid=0
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_measurements.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+
+################################################################################
+##                                                                            
##
+## Copyright (C) 2009 IBM Corporation                                         
##
+##                                                                            
##
+## This program is free software;  you can redistribute it and#or modify      
##
+## it under the terms of the GNU General Public License as published by       
##
+## the Free Software Foundation; either version 2 of the License, or          
##
+## (at your option) any later version.                                        
##
+##                                                                            
##
+## This program is distributed in the hope that it will be useful, but        
##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
##
+## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   
##
+## for more details.                                                          
##
+##                                                                            
##
+## You should have received a copy of the GNU General Public License          
##
+## along with this program;  if not, write to the Free Software               
##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    
##
+##                                                                            
##
+################################################################################
+#
+# File :        ima_measurements.sh
+#
+# Description:  This file verifies measurements are added to the measurement
+#              list based on policy.
+#
+# Author:       Mimi Zohar, [email protected]
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+################################################################################
+init()
+{
+       export TST_TOTAL=3
+       export TCID="init"
+        export TST_COUNT=0
+
+       # check that sha1sum is installed
+       which sha1sum &> /dev/null || RC=$?
+       if [ $RC -ne 0 ]; then
+               tst_brkm TBROK NULL "$TCID: sha1sum not found"
+               return $RC
+       fi
+
+       # verify using default policy
+       if [ ! -f $IMA_DIR/policy ]; then
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: not using default policy"
+       fi
+       return $RC
+}
+
+# Function:     test01
+# Description   - Verify reading a file causes a new measurement to
+#                be added to the IMA measurement list.
+test01()
+{
+       TCID="test01"
+       TST_COUNT=1
+       RC=0
+
+       # Create file test.txt
+       cat > $LTPIMA/test.txt <<-EOF || RC=$?
+       `date` - this is a test file
+       EOF
+       if [ $RC -ne 0 ]; then
+               tst_brkm TBROK $LTPTMP/imalog.$$\
+                "$TCID: Unable to create test file"
+               return $RC
+       fi
+
+       # Calculating the sha1sum of $LTPTMP/test.txt should add
+       # the measurement to the measurement list.
+       # (Assumes SHA1 IMA measurements.)
+       hash=`cat $LTPIMA/test.txt | sha1sum | sed 's/  -//'`
+
+       # Check if the file is measured
+       # (i.e. contained in the ascii measurement list.)
+       cat /sys/kernel/security/ima/ascii_runtime_measurements > \
+                $LTPIMA/measurements
+       sleep 1
+       `grep $hash $LTPIMA/measurements > /dev/null` || RC=$?
+       if [ $RC -ne 0 ]; then
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: TPM ascii measurement list does not contain sha1sum"
+               return $RC
+       else
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: TPM ascii measurement list contains sha1sum"
+       fi
+       return $RC
+}
+
+# Function:     test02
+# Description  - Verify modifying, then reading, a file causes a new
+#                measurement to be added to the IMA measurement list.
+test02()
+{
+       TCID="test02"
+       TST_COUNT=2
+       RC=0
+
+       # Modify test.txt
+       echo `$date` - file modified >> $LTPIMA/test.txt || RC=$?
+
+       # Calculating the sha1sum of $LTPTMP/test.txt should add
+       # the new measurement to the measurement list
+       hash=`cat $LTPIMA/test.txt | sha1sum | sed 's/  -//'`
+
+       # Check if the new measurement exists
+       cat /sys/kernel/security/ima/ascii_runtime_measurements > \
+               $LTPIMA/measurements
+       `grep $hash $LTPIMA/measurements > /dev/null` || RC=$?
+
+       if [ $RC -ne 0 ]; then
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: Modified file not measured"
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: iversion not supported; or not mounted with iversion"
+               return $RC
+       else
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: Modified file measured"
+       fi
+       return $RC
+}
+
+# Function:     test03
+# Description  - Verify files are measured based on policy
+#              (Default policy does not measure user files.)
+test03()
+{
+       TCID="test03"
+       TST_COUNT=3
+       RC=0
+
+       # create file user-test.txt
+       mkdir -m 0700 $LTPIMA/user
+       chown 99.99 $LTPIMA/user
+       cd $LTPIMA/user
+       hash=0
+
+       # As user 99, create and cat the new file
+       sudo -u \#99 sh -c "echo `date` - create test.txt > ./test.txt;
+                               cat ./test.txt > /dev/null"
+
+       # Calculating the hash will add the measurement to the measurement
+       # list, so only calc the hash value after getting the measurement
+       # list.
+       cat /sys/kernel/security/ima/ascii_runtime_measurements > \
+                $LTPIMA/measurements
+       hash=`cat ./test.txt | sha1sum | sed 's/  -//'`
+       cd - >/dev/null
+
+       # Check if the file is measured
+       grep $hash $LTPIMA/measurements > /dev/null || RC=$?
+       if [ $RC -ne 0 ]; then
+               RC=0
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: user file test.txt not measured"
+       else
+               RC=1
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: user file test.txt measured"
+       fi
+       return $RC
+}
+
+# Function:     main
+#
+# Description:  - Execute all tests, exit with test status.
+#
+# Exit:         - zero on success
+#               - non-zero on failure.
+#
+RC=0
+EXIT_VAL=0
+source `dirname $0`\/ima_setup.sh
+setup || exit $RC
+
+init
+test01 || EXIT_VAL=$RC
+test02 || EXIT_VAL=$RC
+test03 || EXIT_VAL=$RC
+exit $EXIT_VAL
Index: ltp-full-20090228/testcases/kernel/security/Makefile
===================================================================
--- ltp-full-20090228.orig/testcases/kernel/security/Makefile
+++ ltp-full-20090228/testcases/kernel/security/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = mmc_security filecaps
+SUBDIRS = mmc_security filecaps integrity
 
 all:
        @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i ; done
Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/Makefile
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/Makefile
@@ -0,0 +1,6 @@
+all:
+install:
+       chmod 755 *.sh; cp *.sh ../../../../../bin/
+
+clean:
+
Index: ltp-full-20090228/runtest/ima
===================================================================
--- /dev/null
+++ ltp-full-20090228/runtest/ima
@@ -0,0 +1,5 @@
+#DESCRIPTION:Integrity Measurement Architecture (IMA)
+ima01   ima_measurements.sh
+ima02   ima_policy.sh
+ima03   ima_tpm.sh
+ima04   ima_violations.sh
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_boot_aggregate.c
@@ -0,0 +1,110 @@
+/*
+* Copyright (c) International Business Machines  Corp., 2009
+*
+* Authors:
+* Mimi Zohar <[email protected]>
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2 of the
+* License.
+*
+* File: ima_boot_aggregate.c
+*
+* Calculate a SHA1 boot aggregate value based on the TPM
+* binary_bios_measurements.
+*
+* Requires openssl; compile with -lcrypto option
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/sha.h>
+
+#define MAX_EVENT_SIZE 500
+#define EVENT_HEADER_SIZE 32
+#define MAX_EVENT_DATA_SIZE (MAX_EVENT_SIZE - EVENT_HEADER_SIZE)
+#define NUM_PCRS 8     /*  PCR registers 0-7 in boot aggregate */
+
+static void *display_sha1_digest(char *pcr)
+{
+       int i;
+
+       for (i = 0; i < 20; i++)
+               printf("%02x", *(pcr + i) & 0xff);
+       printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned char boot_aggregate[SHA_DIGEST_LENGTH];
+       struct {
+               struct {
+                       u_int32_t pcr;
+                       int type;
+                       unsigned char digest[SHA_DIGEST_LENGTH];
+                       u_int16_t len;
+               } header;
+               unsigned char data[MAX_EVENT_DATA_SIZE];
+       } event;
+       struct {
+               unsigned char digest[SHA_DIGEST_LENGTH];
+       } pcr[NUM_PCRS];
+       FILE *fp;
+       int i;
+       int debug = 0;
+       SHA_CTX c;
+
+       if (argc != 2) {
+               printf("format: %s binary_bios_measurement file\n", argv[0]);
+               return 1;
+       }
+       fp = fopen(argv[1], "r");
+       if (!fp) {
+               perror("unable to open pcr file\n");
+               return 1;
+       }
+
+       /* Initialize psuedo PCR registers 0 - 7 */
+       for ( i = 0; i < NUM_PCRS; i++)
+               memset(&pcr[i].digest, 0, SHA_DIGEST_LENGTH);
+
+       /* Extend the pseudo PCRs with the event digest */
+       while (fread(&event, sizeof event.header, 1, fp)) {
+               if (debug) {
+                       printf("%03u ", event.header.pcr);
+                       display_sha1_digest(event.header.digest);
+               }
+               SHA1_Init(&c);
+               SHA1_Update(&c, pcr[event.header.pcr].digest, 20);
+               SHA1_Update(&c, event.header.digest, 20);
+               SHA1_Final(pcr[event.header.pcr].digest, &c);
+               if (event.header.len > MAX_EVENT_DATA_SIZE) {
+                       printf("Error event too long");
+                       break;
+               }
+               fread(event.data, event.header.len, 1, fp);
+       }
+       fclose(fp);
+
+       /* Extend the boot aggregate with the pseudo PCR digest values */
+       memset(&boot_aggregate, 0, SHA_DIGEST_LENGTH);
+       SHA1_Init(&c);
+       for (i = 0; i < NUM_PCRS; i++) {
+               if (debug) {
+                       printf("PCR-%2.2x: ", i);
+                       display_sha1_digest(pcr[i].digest);
+               }
+               SHA1_Update(&c, pcr[i].digest, 20);
+       }
+       SHA1_Final(boot_aggregate, &c);
+
+       printf("boot_aggregate:");
+       display_sha1_digest(boot_aggregate);
+
+       return 0;
+}
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_measure.c
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_measure.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) International Business Machines  Corp., 2008
+ *
+ * Authors:
+ * Reiner Sailer <[email protected]>
+ * Mimi Zohar <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_measure.c
+ *
+ * Calculate the SHA1 aggregate-pcr value based on the IMA runtime
+ * binary measurements.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <openssl/sha.h>
+
+#define TCG_EVENT_NAME_LEN_MAX 255
+
+static int verbose = 0;
+static int validate = 0;
+static int verify = 0;
+
+#define print_info(format, arg...) \
+       if (verbose) \
+               printf(format, ##arg)
+
+static u_int8_t zero[SHA_DIGEST_LENGTH];
+static u_int8_t fox[SHA_DIGEST_LENGTH];
+
+struct event {
+       struct {
+               u_int32_t pcr;
+               u_int8_t digest[SHA_DIGEST_LENGTH];
+               u_int32_t name_len;
+       } header;
+       char name[TCG_EVENT_NAME_LEN_MAX + 1];
+       struct {
+                       u_int8_t digest[SHA_DIGEST_LENGTH];
+               char filename[TCG_EVENT_NAME_LEN_MAX + 1];
+       } ima_data;
+       int filename_len;
+};
+
+static void display_sha1_digest(u_int8_t *digest)
+{
+       int i;
+
+       for (i = 0; i < 20; i++)
+               print_info("%02x", (*(digest + i) & 0xff));
+}
+
+/*
+ * Calculate the sha1 hash of data
+ */
+static void calc_digest(u_int8_t *digest, int len, void *data )
+{
+       SHA_CTX c;
+
+       /* Calc template hash for an ima entry */
+       memset(digest, 0, sizeof *digest);
+       SHA1_Init(&c);
+       SHA1_Update(&c, data, len);
+       SHA1_Final(digest, &c);
+}
+
+static int verify_template_hash(struct event *template)
+{
+       int rc;
+
+       rc = memcmp(fox, template->header.digest, sizeof fox);
+       if (rc != 0) {
+               u_int8_t digest[SHA_DIGEST_LENGTH];
+
+               memset(digest, 0, sizeof digest);
+               calc_digest(digest, sizeof template->ima_data,
+                               &template->ima_data);
+               rc = memcmp(digest, template->header.digest, sizeof digest);
+               return rc != 0 ? 1 : 0;
+       }
+       return 0;
+}
+
+/*
+ * ima_measurements.c - calculate the SHA1 aggregate-pcr value based
+ * on the IMA runtime binary measurements.
+ *
+ * format: ima_measurement [--validate] [--verify] [--verbose]
+ *
+ * --validate: forces validation of the aggregrate pcr value
+ *          for an invalidated PCR. Replace all entries in the
+ *          runtime binary measurement list with 0x00 hash values,
+ *          which indicate the PCR was invalidated, either for
+ *          "a time of measure, time of use"(ToMToU) error, or a
+ *          file open for read was already open for write, with
+ *          0xFF's hash value, when calculating the aggregate
+ *          pcr value.
+ *
+ * --verify: for all IMA template entries in the runtime binary
+ *          measurement list, calculate the template hash value
+ *          and compare it with the actual template hash value.
+ *          Return the number of incorrect hash measurements.
+ *
+ * --verbose: For all entries in the runtime binary measurement
+ *          list, display the template information.
+ *
+ * template info:  list #, PCR-register #, template hash, template name
+ *     IMA info:  IMA hash, filename hint
+ *
+ * Ouput: displays the aggregate-pcr value
+ * Return code: if verification enabled, returns number of verification
+ *             errors.
+ */
+int main(int argc, char *argv[])
+{
+       FILE *fp;
+       struct event template;
+       u_int8_t pcr[SHA_DIGEST_LENGTH];
+       int i, count = 0, len;
+       int failed_count = 0;   /* number of template verifications failed */
+
+       if (argc < 2) {
+               printf("format: %s binary_runtime_measurements" \
+                        " [--validate] [--verbose] [--verify]\n", argv[0]);
+               return 1;
+       }
+
+       for (i = 2; i < argc; i++) {
+               if (strncmp(argv[i], "--validate", 8) == 0)
+                       validate = 1;
+               if (strncmp(argv[i], "--verbose", 7) == 0)
+                       verbose = 1;
+               if (strncmp(argv[i], "--verify", 6) == 0)
+                       verify = 1;
+       }
+
+       fp = fopen(argv[1], "r");
+       if (!fp) {
+               perror("Unable to open file\n");
+               return 1;
+       }
+       memset(pcr, 0, SHA_DIGEST_LENGTH);      /* initial PCR content 0..0 */
+       memset(zero, 0, SHA_DIGEST_LENGTH);
+       memset(fox, 0xff, SHA_DIGEST_LENGTH);
+
+       print_info( "### PCR HASH                                  " \
+                       "TEMPLATE-NAME\n");
+       while (fread(&template.header, sizeof template.header, 1, fp)) {
+               SHA_CTX c;
+
+               /* Extend simulated PCR with new template digest */
+               SHA1_Init(&c);
+               SHA1_Update(&c, pcr, SHA_DIGEST_LENGTH);
+               if (validate) {
+                       if (memcmp(template.header.digest, zero, 20) == 0)
+                               memset(template.header.digest, 0xFF, 20);
+               }
+               SHA1_Update(&c, template.header.digest, 20);
+               SHA1_Final(pcr, &c);
+
+
+               print_info("%3d %03u ", count++, template.header.pcr);
+               display_sha1_digest(template.header.digest);
+               if (template.header.name_len > TCG_EVENT_NAME_LEN_MAX) {
+                       printf("%d ERROR: event name too long!\n",
+                               template.header.name_len);
+                       exit(1);
+               }
+               memset(template.name, 0, sizeof template.name);
+               fread(template.name, template.header.name_len, 1, fp);
+               print_info(" %s ", template.name);
+
+               memset(&template.ima_data, 0, sizeof template.ima_data);
+               fread(&template.ima_data.digest,
+                       sizeof template.ima_data.digest, 1, fp);
+               display_sha1_digest(template.ima_data.digest);
+
+               fread(&template.filename_len,
+                       sizeof template.filename_len, 1, fp);
+               fread(template.ima_data.filename, template.filename_len, 1, fp);
+               print_info(" %s\n", template.ima_data.filename);
+
+               if (verify)
+                       failed_count += verify_template_hash(&template);
+       }
+       fclose(fp);
+
+       verbose=1;
+       print_info("PCRAggr (re-calculated):");
+       display_sha1_digest(pcr);
+       printf("\nreturn failed_count: %d\n", failed_count);
+       return failed_count;
+}
Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_mmap.c
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/src/ima_mmap.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) International Business Machines  Corp., 2009
+ *
+ * Authors:
+ * Mimi Zohar <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_mmap.c
+ *
+ * Open and mmap a file and sleep. Another process will open the
+ * mmapped file in read mode, resulting in a open_writer violation.
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+int main(int argc, char *argv[])
+{
+       int fd;
+       void *file;
+       char *filename;
+       int rc;
+
+       if (argc != 2)
+               printf("%s: filename\n", argv[1]);
+       filename = argv[1];
+
+       fd = open(filename, O_CREAT | O_RDWR, S_IRWXU);
+       if (fd < 0) {
+               perror("open");
+               return(-1);
+       }
+
+       file = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       if (file == (void *) -1) {
+               perror("mmap");
+               return(-1);
+       }
+       close(fd);
+       sleep(30);
+       if (munmap(file, 1024) < 0) {
+               perror("unmap");
+               return(-1);
+       }
+}
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_policy.sh
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_policy.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+################################################################################
+##                                                                            
##
+## Copyright (C) 2009 IBM Corporation                                         
##
+##                                                                            
##
+## This program is free software;  you can redistribute it and#or modify      
##
+## it under the terms of the GNU General Public License as published by       
##
+## the Free Software Foundation; either version 2 of the License, or          
##
+## (at your option) any later version.                                        
##
+##                                                                            
##
+## This program is distributed in the hope that it will be useful, but        
##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
##
+## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   
##
+## for more details.                                                          
##
+##                                                                            
##
+## You should have received a copy of the GNU General Public License          
##
+## along with this program;  if not, write to the Free Software               
##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    
##
+##                                                                            
##
+################################################################################
+#
+# File :        ima_policy.sh
+#
+# Description:  This file tests replacing the default integrity measurement
+#              policy.
+#
+# Author:       Mimi Zohar, [email protected]
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+################################################################################
+init()
+{
+       export TST_TOTAL=3
+       export TCID="init"
+       export TST_COUNT=0
+       RC=0
+
+       # verify using default policy
+       IMA_POLICY=$IMA_DIR/policy
+       if [ ! -f $IMA_POLICY ]; then
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: default policy already replaced"
+                 RC=1
+       fi
+
+       VALID_POLICY=`dirname $0`\/..\/policy/measure.policy
+       if [ ! -f $VALID_POLICY ]; then
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: missing $VALID_POLICY"
+                 RC=1
+       fi
+
+       INVALID_POLICY=`dirname $0`\/..\/policy/measure.policy-invalid
+       if [ ! -f $INVALID_POLICY ]; then
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: missing $INVALID_POLICY"
+                 RC=1
+       fi
+       return $RC
+}
+
+load_policy()
+{
+       exec 2>/dev/null 4>$IMA_POLICY
+       if [ $? -ne 0 ]; then
+               exit 1
+       fi
+
+       cat $1 |
+       while read line ; do
+       {
+               if [ "${line:0:1}" != "#" ] ; then
+                       echo $line >&4 2> /dev/null
+                       if [ $? -ne 0 ]; then
+                               exec 4>&-
+                               RC=1
+                               return $RC
+                       fi
+               fi
+       }
+       done
+}
+
+
+# Function:     test01
+# Description   - Verify invalid policy doesn't replace default policy.
+test01()
+{
+       TCID="test01"
+       TST_COUNT=1
+       RC=0
+
+       load_policy $INVALID_POLICY & p1=$!
+       wait "$p1"; RC=$?
+       if [ $RC -ne 0 ]; then
+               RC=0
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: didn't load invalid policy"
+       else
+               RC=1
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: loaded invalid policy"
+       fi
+       return $RC
+}
+
+# Function:     test02
+# Description  - Verify policy file is opened sequentially, not concurrently
+#                and install new policy
+test02()
+{
+       TCID="test02"
+       TST_COUNT=2
+       RC=0
+
+       load_policy $VALID_POLICY & p1=$!  # forked process 1
+       load_policy $VALID_POLICY & p2=$!  # forked process 2
+       wait "$p1"; RC1=$?
+       wait "$p2"; RC2=$?
+       RC=$((`expr $RC1 + $RC2`))
+       if [ $RC -eq 1 ]; then
+               RC=0
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: replaced default measurement policy"
+       elif [ $RC -eq 0 ]; then
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: measurement policy opened concurrently"
+       else
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: problems opening measurement policy"
+       fi
+       return 0
+}
+
+# Function:     test03
+# Description  - Verify can't load another measurement policy.
+test03()
+{
+       TCID="test03"
+       TST_COUNT=3
+       RC=0
+
+       load_policy $INVALID_POLICY & p1=$!
+       wait "$p1"; RC=$?
+       if [ $RC -ne 0 ]; then
+               RC=0
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: didn't replace valid policy"
+       else
+               RC=1
+               tst_res TFAIL $LTPTMP/imalog.$$ "$TCID: replaced valid policy"
+       fi
+       return $RC
+}
+
+# Function:     main
+#
+# Description:  - Execute all tests, exit with test status.
+#
+# Exit:         - zero on success
+#               - non-zero on failure.
+#
+RC=0    # Return value from setup, init, and test functions.
+EXIT_VAL=0
+
+source `dirname $0`\/ima_setup.sh
+setup || exit $RC
+
+init || exit $RC
+test01 || EXIT_VAL=$RC
+test02 || EXIT_VAL=$RC
+test03 || EXIT_VAL=$RC
+exit $EXIT_VAL
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_setup.sh
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_setup.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+################################################################################
+##                                                                            
##
+## Copyright (C) 2009 IBM Corporation                                         
##
+##                                                                            
##
+## This program is free software;  you can redistribute it and#or modify      
##
+## it under the terms of the GNU General Public License as published by       
##
+## the Free Software Foundation; either version 2 of the License, or          
##
+## (at your option) any later version.                                        
##
+##                                                                            
##
+## This program is distributed in the hope that it will be useful, but        
##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
##
+## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   
##
+## for more details.                                                          
##
+##                                                                            
##
+## You should have received a copy of the GNU General Public License          
##
+## along with this program;  if not, write to the Free Software               
##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    
##
+##                                                                            
##
+################################################################################
+#
+# File :        ima_setup.sh
+#
+# Description:  setup/cleanup routines for the integrity tests.
+#
+# Author:       Mimi Zohar, [email protected]
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+################################################################################
+mount_sysfs()
+{
+       SYSFS=`mount | grep sysfs` || RC=$?
+       if [ $RC -eq 1 ]; then
+               SYSFS=/sys
+               mkdir -p $SYSFS
+               `mount -t sysfs sysfs $SYSFS`
+               RC=$?
+               return $RC
+       else
+               SYSFS=`echo $SYSFS |  sed 's/sysfs on //' | sed 's/ type .*//'`
+       fi
+       return 0
+}
+
+mount_securityfs()
+{
+       SECURITYFS=`mount | grep securityfs` || RC=$?
+       if [ $RC == 1 ]; then
+               SECURITYFS=$SYSFS/kernel/security
+               `mkdir -p $SECURITYFS`
+               `mount -t securityfs securityfs $SECURITYFS`
+               RC=$?
+               return $RC
+       else
+               SECURITYFS=`echo $SECURITYFS |  sed 's/securityfs on //' \
+                       | sed 's/ type .*//'`
+       fi
+       return 0
+}
+
+setup()
+{
+       export TST_TOTAL=1
+       export TCID="setup"
+        export TST_COUNT=0
+
+       trap "cleanup" 0
+       if [ -z $TMP ]; then
+               LTPTMP=/tmp
+       else
+               LTPTMP=${TMP}
+       fi
+       if [ -z $LTPBIN ]; then
+               LTPBIN=../../../../../bin
+       fi
+
+       # Must be root
+       if [ $UID -ne 0 ]; then
+               tst_brkm TBROK $LTPTMP/imalog.$$ \
+                "$TCID: Must be root to execute test"
+               return 1
+       fi
+
+       if [ -z $TMP ]; then
+               LTPTMP=/tmp
+       else
+               LTPTMP=${TMP}
+       fi
+
+       # create the temporary directory used by this testcase
+       LTPIMA=$LTPTMP/ima
+       umask 077
+       mkdir $LTPIMA &>/dev/null || RC=$?
+       if [ $RC -ne 0 ]; then
+               tst_brk TBROK "$TCID: Unable to create temporary directory"
+               return $RC
+       fi
+
+       # mount sysfs if it is not already mounted
+       mount_sysfs || RC=$?
+       if [ $RC -ne 0 ]; then
+               tst_brkm TBROK $LTPTMP/imalog.$$ "$TCID: cannot mount sysfs"
+               return $RC
+       fi
+
+       # mount securityfs if it is not already mounted
+       mount_securityfs || RC=$?
+       if [ $RC -ne 0 ]; then
+               tst_brkm TBROK $LTPTMP/imalog.$$ "$TCID: cannot mount 
securityfs"
+               return $RC
+       fi
+
+       SECURITYFS=`echo $SECURITYFS |  sed 's/securityfs on //' \
+               | sed 's/ type .*//'`
+
+       # IMA must be configured in the kernel
+       IMA_DIR=$SECURITYFS/ima
+       if [ ! -d $IMA_DIR ]; then
+               tst_brkm TBROK $LTPTMP/imalog.$$\
+                "INIT: IMA not enabled in kernel"
+               RC=1
+       fi
+       return $RC
+}
+
+# Function:     cleanup
+#
+# Description   - remove temporary files and directories.
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+cleanup()
+{
+       tst_resm TINFO "CLEAN: removing $LTPIMA"
+       rm -rf $LTPIMA || RC $?
+       return $RC
+}
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
@@ -0,0 +1,164 @@
+#!/bin/sh
+
+################################################################################
+##                                                                            
##
+## Copyright (C) 2009 IBM Corporation                                         
##
+##                                                                            
##
+## This program is free software;  you can redistribute it and#or modify      
##
+## it under the terms of the GNU General Public License as published by       
##
+## the Free Software Foundation; either version 2 of the License, or          
##
+## (at your option) any later version.                                        
##
+##                                                                            
##
+## This program is distributed in the hope that it will be useful, but        
##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
##
+## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   
##
+## for more details.                                                          
##
+##                                                                            
##
+## You should have received a copy of the GNU General Public License          
##
+## along with this program;  if not, write to the Free Software               
##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    
##
+##                                                                            
##
+################################################################################
+#
+# File :        ima_tpm.sh
+#
+# Description:  This file verifies the boot and PCR aggregates
+#
+# Author:       Mimi Zohar, [email protected]
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+################################################################################
+init()
+{
+       export TST_TOTAL=3
+       export TCID="init"
+       export TST_COUNT=0
+       RC=0
+}
+
+# Function:     test01
+# Description   - Verify boot aggregate value is correct
+test01()
+{
+       TCID="test01"
+       TST_COUNT=1
+       RC=0
+
+       # IMA boot aggregate
+       ima_measurements=$SECURITYFS/ima/ascii_runtime_measurements
+       read line < $ima_measurements
+
+       # verify TPM is available and enabled.
+       tpm_bios=$SECURITYFS/tpm0/binary_bios_measurements
+       if [ ! -f $tpm_bios ]; then
+               tst_res TINFO $LTPTMP/imalog.$$\
+                "$TCID: no TPM, TPM not builtin kernel, or TPM not enabled"
+
+               [ "${line:49:40}" -eq 0 ] || RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: bios boot aggregate is 0."
+               else
+                       tst_res TFAIL $LTPTMP/imalog.$$\
+                        "$TCID: bios boot aggregate is not 0."
+               fi
+       else
+               boot_aggregate=`ima_boot_aggregate $tpm_bios`
+
+               [ "${line:48:40}" == "${boot_aggregate:15:40}" ] ||  RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: bios aggregate matches IMA boot aggregate."
+               else
+                       tst_res TFAIL $LTPTMP/imalog.$$\
+                        "$TCID: bios aggregate does not match IMA boot " \
+                               "aggregate."
+               fi
+       fi
+       return $RC
+}
+
+# Probably cleaner to programmatically read the PCR values directly
+# from the TPM, but that would require a TPM library. For now, use
+# the PCR values from /sys/devices.
+validate_pcr()
+{
+       aggregate_pcr=`ima_measure --validate`
+       dev_pcrs=$1
+       while read line ; do
+               if [ "${line:0:6}" == "PCR-10" ]; then
+                       [ "${line:8:40}" == "${aggregate_pcr:0:40}" ]
+                               RC=$?
+               fi
+       done < $dev_pcrs
+       return $RC
+}
+
+# Function:     test02
+# Description  - Verify ima calculated aggregate PCR values matches
+#                actual PCR value.
+test02()
+{
+       TCID="test02"
+       TST_COUNT=2
+       RC=0
+
+#      Would be nice to know where the PCRs are located.  Is this safe?
+       PCRS_PATH=`find /$SYSFS/devices/ | grep pcrs` || RC=$?
+       if [ $RC -eq 0 ]; then
+               validate_pcr $PCRS_PATH || RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: aggregate PCR value matches real PCR value."
+               else
+                       tst_res TFAIL $LTPTMP/imalog.$$\
+                        "$TCID: aggregate PCR value does not match" \
+                        " real PCR value."
+               fi
+       else
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: TPM not enabled, no PCR value to validate"
+       fi
+       return $RC
+}
+
+# Function:     test03
+# Description  - Verify template hash value for IMA entry is correct.
+test03()
+{
+       TCID="test03"
+       TST_COUNT=3
+       RC=0
+
+       aggregate_pcr=`ima_measure --verify --validate` > /dev/null
+       RC=$?
+       if [ $RC -eq 0 ]; then
+               tst_res TPASS $LTPTMP/imalog.$$\
+                "$TCID: verified IMA template hash values."
+       else
+               tst_res TFAIL $LTPTMP/imalog.$$\
+                "$TCID: error verifing IMA template hash values."
+       fi
+       return $RC
+}
+
+# Function:     main
+#
+# Description:  - Execute all tests, exit with test status.
+#
+# Exit:         - zero on success
+#               - non-zero on failure.
+#
+RC=0    # Return value from setup, and test functions.
+EXIT_VAL=0
+
+# set the testcases/bin directory
+source `dirname $0`\/ima_setup.sh
+setup || exit $RC
+
+init || EXIT_VAL=$RC
+test01 || EXIT_VAL=$RC
+test02 || EXIT_VAL=$RC
+test03 || EXIT_VAL=$RC
+exit $EXIT_VAL
Index: 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_violations.sh
===================================================================
--- /dev/null
+++ 
ltp-full-20090228/testcases/kernel/security/integrity/ima/tests/ima_violations.sh
@@ -0,0 +1,182 @@
+#!/bin/sh
+################################################################################
+##                                                                            
##
+## Copyright (C) 2009 IBM Corporation                                         
##
+##                                                                            
##
+## This program is free software;  you can redistribute it and#or modify      
##
+## it under the terms of the GNU General Public License as published by       
##
+## the Free Software Foundation; either version 2 of the License, or          
##
+## (at your option) any later version.                                        
##
+##                                                                            
##
+## This program is distributed in the hope that it will be useful, but        
##
+## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
##
+## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   
##
+## for more details.                                                          
##
+##                                                                            
##
+## You should have received a copy of the GNU General Public License          
##
+## along with this program;  if not, write to the Free Software               
##
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA    
##
+##                                                                            
##
+################################################################################
+#
+# File :        ima_violations.sh
+#
+# Description:  This file tests ToMToU and open_writer violations invalidate
+#              the PCR and are logged.
+#
+# Author:       Mimi Zohar, [email protected]
+#
+# Return        - zero on success
+#               - non zero on failure. return value from commands ($RC)
+################################################################################
+
+open_file_read()
+{
+       exec 3< $1
+       if [ $? -ne 0 ]; then
+               exit 1
+       fi
+}
+
+close_file_read()
+{
+       exec 3>&-
+}
+
+open_file_write()
+{
+       exec 4> $1
+       if [ $? -ne 0 ]; then
+               exit 1
+       echo 'testing, testing, ' >&4
+       fi
+}
+
+close_file_write()
+{
+       exec 4>&-
+}
+
+init()
+{
+       export TST_TOTAL=3
+       export TCID="init"
+       export TST_COUNT=0
+       RC=0
+}
+
+# Function:     test01
+# Description   - Verify ToMToU violation
+test01()
+{
+       TCID="test01"
+       TST_COUNT=1
+       RC=0
+
+       ima_violations=$SECURITYFS/ima/violations
+       read num_violations < $ima_violations
+
+       TMPFN=$LTPIMA/test.txt-$$
+       open_file_write $TMPFN
+       open_file_read $TMPFN
+       close_file_read
+       close_file_write
+       read num_violations_new < $ima_violations
+       num=$((`expr $num_violations_new - $num_violations`))
+       if [ $num -gt 0 ]; then
+               tail /var/log/audit/audit.log | grep test.txt-$$ | \
+                       grep 1>/dev/null 'open_writers' || RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: open_writers violation added"
+                       return $RC
+               fi
+       fi
+       tst_res TFAIL $LTPTMP/imalog.$$\
+        "$TCID: open_writers violation not added"
+       return $RC
+}
+
+# Function:     test02
+# Description  - Verify open writers violation
+test02()
+{
+       TCID="test02"
+       TST_COUNT=2
+       RC=0
+
+       ima_violations=$SECURITYFS/ima/violations
+       read num_violations < $ima_violations
+
+       TMPFN=$LTPIMA/test.txt-$$
+       open_file_read $TMPFN
+       open_file_write $TMPFN
+       close_file_write
+       close_file_read
+       read num_violations_new < $ima_violations
+       num=$((`expr $num_violations_new - $num_violations`))
+       if [ $num -gt 0 ]; then
+               tail /var/log/audit/audit.log | grep test.txt-$$ | \
+                       grep 'ToMToU' 1>/dev/null || RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: ToMToU violation added"
+                       return $RC
+               fi
+       fi
+       tst_res TFAIL $LTPTMP/imalog.$$ "$TCID: ToMToU violation not added"
+       return $RC
+}
+
+# Function:     test03
+# Description  - verify open_writers using mmapped files
+test03()
+{
+       TCID="test03"
+       TST_COUNT=3
+       RC=0
+
+       ima_violations=$SECURITYFS/ima/violations
+       read num_violations < $ima_violations
+
+       TMPFN=$LTPIMA/test.txtb-$$
+       mkdir -p $LTPIMA
+       echo 'testing testing ' > $TMPFN
+       ima_mmap $TMPFN & p1=$!
+       sleep 1         # got to wait for ima_mmap to mmap the file
+       open_file_read $TMPFN
+       read num_violations_new < $ima_violations
+       num=$((`expr $num_violations_new - $num_violations`))
+       if [ $num -gt 0 ]; then
+               tail /var/log/audit/audit.log | grep test.txtb-$$ | \
+                       grep 1>/dev/null 'open_writers' || RC=$?
+               if [ $RC -eq 0 ]; then
+                       tst_res TPASS $LTPTMP/imalog.$$\
+                        "$TCID: mmapped open_writers violation added"
+                       return $RC
+               fi
+       fi
+       tst_res TFAIL $LTPTMP/imalog.$$\
+        "$TCID: mmapped open_writers violation not added"
+       close_file_read
+       return $RC
+}
+
+# Function:     main
+#
+# Description:  - Execute all tests, exit with test status.
+#
+# Exit:         - zero on success
+#               - non-zero on failure.
+#
+RC=0    # Return value from setup, init, and test functions.
+EXIT_VAL=0
+
+source `dirname $0`\/ima_setup.sh
+setup || exit $RC
+
+init || exit $RC
+test01 || EXIT_VAL=$RC
+test02 || EXIT_VAL=$RC
+test03 || EXIT_VAL=$RC
+exit $EXIT_VAL
Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/Makefile
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/policy/Makefile
@@ -0,0 +1,6 @@
+all:
+install:
+       mkdir -p ../../../../../policy; cp measure* ../../../../../policy/
+
+clean:
+
Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/src/Makefile
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/src/Makefile
@@ -0,0 +1,14 @@
+ifeq ($(shell openssl version > /dev/null 2>&1 || echo yes),yes)
+TARGETS=ima_mmap
+else
+TARGETS=ima_measure ima_boot_aggregate ima_mmap
+LDLIBS += -lcrypto
+endif
+
+all: $(TARGETS)
+
+install:
+       @set -e; for i in $(TARGETS); do ln -f $$i ../../../../../bin/$$i; done
+
+clean:
+       rm -f $(TARGETS)
Index: ltp-full-20090228/testcases/kernel/security/integrity/ima/Makefile
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/ima/Makefile
@@ -0,0 +1,11 @@
+SUBDIRS = src tests policy
+
+all:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i ; done
+
+install:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i install ; done
+
+clean:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean ; done
+
Index: ltp-full-20090228/testcases/kernel/security/integrity/Makefile
===================================================================
--- /dev/null
+++ ltp-full-20090228/testcases/kernel/security/integrity/Makefile
@@ -0,0 +1,11 @@
+SUBDIRS = ima
+
+all:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i ; done
+
+install:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i install ; done
+
+clean:
+       @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean ; done
+



------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H
_______________________________________________
Ltp-list mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ltp-list

Reply via email to