Blob Blame History Raw
diff --git a/cmd/pk12util/pk12util.c b/cmd/pk12util/pk12util.c
--- a/cmd/pk12util/pk12util.c
+++ b/cmd/pk12util/pk12util.c
@@ -660,16 +660,27 @@ P12U_ExportPKCS12Object(char *nn, char *
     }
 
     /*	Password to use for PKCS12 file.  */
     pwitem = P12U_GetP12FilePassword(PR_TRUE, p12FilePw);
     if (!pwitem) {
         goto loser;
     }
 
+    /* we are passing UTF8, drop the NULL in the normal password value.
+     * UCS2 conversion will add it back if necessary. This only affects
+     * password > Blocksize of the Hash function and pkcs5v2 pbe (if password
+     * <=Blocksize then the password is zero padded anyway, so an extra NULL
+     * at the end has not effect). This is allows us to work with openssl and
+     * gnutls. Older versions of NSS already fail to decrypt long passwords
+     * in this case, so we aren't breaking anyone with this code */
+    if ((pwitem->len > 1)  && (!pwitem->data[pwitem->len-1])) {
+        pwitem->len--;
+    }
+
     p12cxt = p12u_InitContext(PR_FALSE, outfile);
     if (!p12cxt) {
         SECU_PrintError(progName, "Initialization failed: %s", outfile);
         pk12uErrno = PK12UERR_INIT_FILE;
         goto loser;
     }
 
     if (certlist) {
diff --git a/lib/pkcs12/p12local.c b/lib/pkcs12/p12local.c
--- a/lib/pkcs12/p12local.c
+++ b/lib/pkcs12/p12local.c
@@ -903,31 +903,35 @@ sec_pkcs12_find_object(SEC_PKCS12SafeCon
             i++;
         }
     }
 
     PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
     return NULL;
 }
 
-/* this function converts a password to unicode and encures that the
- * required double 0 byte be placed at the end of the string
+/* this function converts a password to unicode and ensures that the
+ * required double 0 byte be placed at the end of the string (if zeroTerm
+ * is set), or the 0 bytes at the end are dropped (if zeroTerm is not set).
  */
 PRBool
 sec_pkcs12_convert_item_to_unicode(PLArenaPool *arena, SECItem *dest,
                                    SECItem *src, PRBool zeroTerm,
                                    PRBool asciiConvert, PRBool toUnicode)
 {
     PRBool success = PR_FALSE;
+    int bufferSize;
+
     if (!src || !dest) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return PR_FALSE;
     }
 
-    dest->len = src->len * 3 + 2;
+    bufferSize = src->len * 3 + 2;
+    dest->len = bufferSize;
     if (arena) {
         dest->data = (unsigned char *)PORT_ArenaZAlloc(arena, dest->len);
     } else {
         dest->data = (unsigned char *)PORT_ZAlloc(dest->len);
     }
 
     if (!dest->data) {
         dest->len = 0;
@@ -951,34 +955,44 @@ sec_pkcs12_convert_item_to_unicode(PLAre
         if (!arena) {
             PORT_Free(dest->data);
             dest->data = NULL;
             dest->len = 0;
         }
         return PR_FALSE;
     }
 
-    if ((dest->len >= 2) &&
-        (dest->data[dest->len - 1] || dest->data[dest->len - 2]) && zeroTerm) {
-        if (dest->len + 2 > 3 * src->len) {
-            if (arena) {
-                dest->data = (unsigned char *)PORT_ArenaGrow(arena,
-                                                             dest->data, dest->len,
-                                                             dest->len + 2);
-            } else {
-                dest->data = (unsigned char *)PORT_Realloc(dest->data,
-                                                           dest->len + 2);
+    /* in some cases we need to add NULL terminations and in others
+     * we need to drop null terminations */
+    if (zeroTerm) {
+        /* unicode adds two nulls a the end */
+        if (toUnicode) {
+            if ((dest->len >= 2) &&
+                (dest->data[dest->len - 1] || dest->data[dest->len - 2])) {
+                /* we've already allocated space for these new NULLs */
+                PORT_Assert(dest->len + 2 <= bufferSize);
+                dest->len += 2;
+                dest->data[dest->len - 1] = dest->data[dest->len - 2] = 0;
             }
-
-            if (!dest->data) {
-                return PR_FALSE;
+        /* ascii/utf-8 adds just 1 */
+        } else if ((dest->len >= 1) && dest->data[dest->len-1]) {
+            PORT_Assert(dest->len + 1 <= bufferSize);
+            dest->len ++;
+            dest->data[dest->len-1] = 0;
+        }
+    } else {
+    /* handle the drop case, no need to do any allocations here. */
+        if (toUnicode) {
+            while ((dest->len >=2) && !dest->data[dest->len - 1] &&
+                   !dest->data[dest->len - 2]) {
+                dest->len -= 2;
             }
+        } else while (dest->len && !dest->data[dest->len-1]) {
+            dest->len--;
         }
-        dest->len += 2;
-        dest->data[dest->len - 1] = dest->data[dest->len - 2] = 0;
     }
 
     return PR_TRUE;
 }
 
 PRBool
 sec_pkcs12_is_pkcs12_pbe_algorithm(SECOidTag algorithm)
 {
@@ -1006,27 +1020,28 @@ sec_pkcs12_is_pkcs12_pbe_algorithm(SECOi
     }
 }
 
 /* this function decodes a password from Unicode if necessary,
  * according to the PBE algorithm.
  *
  * we assume that the pwitem is already encoded in Unicode by the
  * caller.  if the encryption scheme is not the one defined in PKCS
- * #12, decode the pwitem back into UTF-8. */
+ * #12, decode the pwitem back into UTF-8. NOTE: UTF-8 strings are
+ * used in the PRF without the trailing NULL */
 PRBool
 sec_pkcs12_decode_password(PLArenaPool *arena,
                            SECItem *result,
                            SECOidTag algorithm,
                            const SECItem *pwitem)
 {
     if (!sec_pkcs12_is_pkcs12_pbe_algorithm(algorithm))
         return sec_pkcs12_convert_item_to_unicode(arena, result,
                                                   (SECItem *)pwitem,
-                                                  PR_TRUE, PR_FALSE, PR_FALSE);
+                                                  PR_FALSE, PR_FALSE, PR_FALSE);
 
     return SECITEM_CopyItem(arena, result, pwitem) == SECSuccess;
 }
 
 /* this function encodes a password into Unicode if necessary,
  * according to the PBE algorithm.
  *
  * we assume that the pwitem holds a raw password.  if the encryption
diff --git a/tests/common/init.sh b/tests/common/init.sh
--- a/tests/common/init.sh
+++ b/tests/common/init.sh
@@ -78,25 +78,27 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
 
         CERT_EXTENSIONS_DIR=${HOSTDIR}/cert_extensions
         STAPLINGDIR=${HOSTDIR}/stapling
         NOLOGINDIR=${HOSTDIR}/nologin
         SSLGTESTDIR=${HOSTDIR}/ssl_gtests
         GTESTDIR=${HOSTDIR}/gtests
 
         PWFILE=${HOSTDIR}/tests.pw
+        LONGPWFILE=${HOSTDIR}/tests.longpw
         EMPTY_FILE=${HOSTDIR}/tests_empty
         NOISE_FILE=${HOSTDIR}/tests_noise
         CORELIST_FILE=${HOSTDIR}/clist
 
         FIPSPWFILE=${HOSTDIR}/tests.fipspw
         FIPSBADPWFILE=${HOSTDIR}/tests.fipsbadpw
         FIPSP12PWFILE=${HOSTDIR}/tests.fipsp12pw
 
         echo nss > ${PWFILE}
+        echo "nss123456789012345678901234567890123456789012345678901234567890_" > ${LONGPWFILE}
         echo > ${EMPTY_FILE}
         echo "fIps140" > ${FIPSPWFILE}
         echo "fips104" > ${FIPSBADPWFILE}
         echo "pKcs12fips140" > ${FIPSP12PWFILE}
 
         noise
 
         P_SERVER_CADIR=${SERVER_CADIR}
@@ -656,16 +658,17 @@ if [ -z "${INIT_SOURCED}" -o "${INIT_SOU
         P_R_NOLOGINDIR="multiaccess:${D_NOLOGIN}"
         P_R_EXT_SERVERDIR="multiaccess:${D_EXT_SERVER}"
         P_R_EXT_CLIENTDIR="multiaccess:${D_EXT_CLIENT}"
         P_R_IMPLICIT_INIT_DIR="multiaccess:${D_IMPLICIT_INIT}"
         P_R_RSAPSSDIR="multiaccess:${D_RSAPSS}"
     fi
 
     R_PWFILE=../tests.pw
+    R_LONGPWFILE=../tests.longpw
     R_EMPTY_FILE=../tests_empty
     R_NOISE_FILE=../tests_noise
 
     R_FIPSPWFILE=../tests.fipspw
     R_FIPSBADPWFILE=../tests.fipsbadpw
     R_FIPSP12PWFILE=../tests.fipsp12pw
 
     trap "Exit $0 Signal_caught" 2 3
diff --git a/tests/tools/tools.sh b/tests/tools/tools.sh
--- a/tests/tools/tools.sh
+++ b/tests/tools/tools.sh
@@ -382,16 +382,40 @@ tools_p12_export_list_import_with_defaul
   check_tmpfile
 
   echo "$SCRIPTNAME: Listing Alice's pk12 EC file -----------------"
   echo "pk12util -l Alice-ec.p12 -w ${R_PWFILE}"
   ${BINDIR}/pk12util -l Alice-ec.p12 -w ${R_PWFILE} 2>&1
   ret=$?
   html_msg $ret 0 "Listing Alice's pk12 EC file (pk12util -l)"
   check_tmpfile
+
+  echo "$SCRIPTNAME: Exporting Alice's email EC cert & key with long pw------"
+  echo "pk12util -o Alice-ec-long.p12 -n \"Alice-ec\" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \\"
+  echo "         -w ${R_LONGPWFILE}"
+  ${BINDIR}/pk12util -o Alice-ec-long.p12 -n "Alice-ec" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \
+       -w ${R_LONGPWFILE} 2>&1
+  ret=$?
+  html_msg $ret 0 "Exporting Alice's email EC cert & key with long pw (pk12util -o)"
+  check_tmpfile
+  verify_p12 Alice-ec-long.p12 "default" "default" "default"
+
+  echo "$SCRIPTNAME: Importing Alice's email EC cert & key with long pw-----"
+  echo "pk12util -i Alice-ec-long.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_LONGPWFILE}"
+  ${BINDIR}/pk12util -i Alice-ec-long.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_LONGPWFILE} 2>&1
+  ret=$?
+  html_msg $ret 0 "Importing Alice's email EC cert & key with long pw (pk12util -i)"
+  check_tmpfile
+
+  echo "$SCRIPTNAME: Listing Alice's pk12 EC file with long pw ------------"
+  echo "pk12util -l Alice-ec-long.p12 -w ${R_LONGPWFILE}"
+  ${BINDIR}/pk12util -l Alice-ec-long.p12 -w ${R_LONGPWFILE} 2>&1
+  ret=$?
+  html_msg $ret 0 "Listing Alice's pk12 EC file with long pw (pk12util -l)"
+  check_tmpfile
 }
 
 tools_p12_import_old_files()
 {
   echo "$SCRIPTNAME: Importing PKCS#12 files created with older NSS --------------"
   echo "pk12util -i TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
   ${BINDIR}/pk12util -i ${TOOLSDIR}/data/TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
   ret=$?