vpd: restrict the characters of serial numbers

Now serial numbers can only use A-Z, a-z, 0-9 and dash (-) characters.

BUG=chromium:1250434
TEST=make test
BRANCH=None

Change-Id: Id736b92a664b97192a584c2dbea62bfa3eb448e4
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vpd/+/3181461
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Tested-by: Stimim Chen <stimim@chromium.org>
Commit-Queue: Stimim Chen <stimim@chromium.org>
diff --git a/tests/test_basic.sh b/tests/test_basic.sh
index 3e24f6e..6f98d6f 100755
--- a/tests/test_basic.sh
+++ b/tests/test_basic.sh
@@ -89,6 +89,17 @@
   RUN $VPD_ERR_PARAM "$BINARY -f $BIOS -d NONE"
 
   #
+  # Test adding strings with double quote (")
+  # Serial number is a special case.
+  RUN $VPD_ERR_PARAM "$BINARY -f $BIOS -s serial_number=quote\\\"quote"
+  # For other keys, double quote can be used.
+  RUN $VPD_OK "$BINARY -f $BIOS -s XYZ=quote\\\"quote"
+  # Make sure the value is as expected
+  RUN $GREP_OK "$BINARY -f $BIOS -l | grep XYZ | grep quote\\\"quote"
+  # Clean up.
+  RUN $VPD_OK "$BINARY -f $BIOS -d XYZ"
+
+  #
   # Delete single.
   # expect SUCCESS and nothing left.
   RUN $VPD_OK "$BINARY -f $BIOS -d ABC"
diff --git a/vpd.c b/vpd.c
index 1adac95..71aeb44 100644
--- a/vpd.c
+++ b/vpd.c
@@ -304,6 +304,59 @@
 
 
 /*
+ * Check if key and value are compliant to recommended format.
+ * For the checker of the key, see the function |checkKeyName|.
+ * If key is "serial_number" or "mlb_serial_number", the value should only
+ * contain characters a-z, A-Z, 0-9 or dash (-).
+ */
+static vpd_err_t checkKeyValuePair(const uint8_t *key, const uint8_t *value) {
+  vpd_err_t retval = VPD_OK;
+  int is_serial_number = 0;
+  size_t value_len = 0;
+  unsigned char c;
+
+  retval = checkKeyName(key);
+  if (retval != VPD_OK)
+    return retval;
+
+  if (strncmp("serial_number",
+              (const char*)key,
+              sizeof("serial_number")) == 0 ||
+      strncmp("mlb_serial_number",
+              (const char*)key,
+              sizeof("mlb_serial_number")) == 0)
+    is_serial_number = 1;
+
+  if (is_serial_number) {
+    // For serial numbers, we only allow a-z, A-Z, 0-9 and dash (-).
+    for (value_len = 0; (c = value[value_len]); value_len++) {
+      if (!(isalnum(c) || c == '-')) {
+        fprintf(stderr, "[ERROR] serial number does not allow char [%c].\n", c);
+        return VPD_ERR_PARAM;
+      }
+    }
+
+    if (value_len == 0) {
+      fprintf(stderr, "[ERROR] serial number cannot be empty.\n");
+      return VPD_ERR_PARAM;
+    }
+
+    if (value[0] == '-') {
+      fprintf(stderr, "[ERROR] serial number cannot start with [-].\n");
+      return VPD_ERR_PARAM;
+    }
+
+    if (value[value_len - 1] == '-') {
+      fprintf(stderr, "[ERROR] serial number cannot end with [-].\n");
+      return VPD_ERR_PARAM;
+    }
+  }
+
+  return VPD_OK;
+}
+
+
+/*
  * Given a key=value string, this function parses it and adds to arugument
  * pair container. The 'value' can be stored in a base64 format file, in this
  * case the value field is the file name.
@@ -340,7 +393,7 @@
     }
   }
 
-  retval = checkKeyName(key);
+  retval = checkKeyValuePair(key, value);
   if (retval == VPD_OK)
     setString(&set_argument, key, value, pad_value_len);