Change the return type of EVP_EncodeUpdate
[oweals/openssl.git] / crypto / pem / pem_lib.c
index 56865541aaffa6d2db6a8c5e8875b548df0f9444..8965fda8d0207e6f1abbfcd7e16951bdfe136985 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <stdio.h>
 #include <ctype.h>
+#include <string.h>
 #include "internal/cryptlib.h"
 #include <openssl/buffer.h>
 #include <openssl/objects.h>
@@ -30,21 +31,23 @@ int pem_check_suffix(const char *pem_str, const char *suffix);
 int PEM_def_callback(char *buf, int num, int w, void *key)
 {
 #if defined(OPENSSL_NO_STDIO) || defined(OPENSSL_NO_UI)
-    /*
-     * We should not ever call the default callback routine from windows.
-     */
-    PEMerr(PEM_F_PEM_DEF_CALLBACK, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return (-1);
+    int i;
 #else
     int i, j;
     const char *prompt;
+#endif
+
     if (key) {
         i = strlen(key);
         i = (i > num) ? num : i;
         memcpy(buf, key, i);
-        return (i);
+        return i;
     }
 
+#if defined(OPENSSL_NO_STDIO) || defined(OPENSSL_NO_UI)
+    PEMerr(PEM_F_PEM_DEF_CALLBACK, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return -1;
+#else
     prompt = EVP_get_pw_prompt();
     if (prompt == NULL)
         prompt = "Enter PEM pass phrase:";
@@ -60,7 +63,7 @@ int PEM_def_callback(char *buf, int num, int w, void *key)
         if (i != 0) {
             PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
             memset(buf, 0, (unsigned int)num);
-            return (-1);
+            return -1;
         }
         j = strlen(buf);
         if (min_len && j < min_len) {
@@ -70,7 +73,7 @@ int PEM_def_callback(char *buf, int num, int w, void *key)
         } else
             break;
     }
-    return (j);
+    return j;
 #endif
 }
 
@@ -389,115 +392,153 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
 int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *plen,
                   pem_password_cb *callback, void *u)
 {
-    int i = 0, j, o, klen;
-    long len;
+    int ok;
+    int keylen;
+    long len = *plen;
+    int ilen = (int) len;       /* EVP_DecryptUpdate etc. take int lengths */
     EVP_CIPHER_CTX *ctx;
     unsigned char key[EVP_MAX_KEY_LENGTH];
     char buf[PEM_BUFSIZE];
 
-    len = *plen;
+#if LONG_MAX > INT_MAX
+    /* Check that we did not truncate the length */
+    if (len > INT_MAX) {
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_HEADER_TOO_LONG);
+        return 0;
+    }
+#endif
 
     if (cipher->cipher == NULL)
-        return (1);
+        return 1;
     if (callback == NULL)
-        klen = PEM_def_callback(buf, PEM_BUFSIZE, 0, u);
+        keylen = PEM_def_callback(buf, PEM_BUFSIZE, 0, u);
     else
-        klen = callback(buf, PEM_BUFSIZE, 0, u);
-    if (klen <= 0) {
+        keylen = callback(buf, PEM_BUFSIZE, 0, u);
+    if (keylen <= 0) {
         PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_PASSWORD_READ);
-        return (0);
+        return 0;
     }
 #ifdef CHARSET_EBCDIC
     /* Convert the pass phrase from EBCDIC */
-    ebcdic2ascii(buf, buf, klen);
+    ebcdic2ascii(buf, buf, keylen);
 #endif
 
     if (!EVP_BytesToKey(cipher->cipher, EVP_md5(), &(cipher->iv[0]),
-                        (unsigned char *)buf, klen, 1, key, NULL))
+                        (unsigned char *)buf, keylen, 1, key, NULL))
         return 0;
 
-    j = (int)len;
     ctx = EVP_CIPHER_CTX_new();
     if (ctx == NULL)
         return 0;
-    o = EVP_DecryptInit_ex(ctx, cipher->cipher, NULL, key, &(cipher->iv[0]));
-    if (o)
-        o = EVP_DecryptUpdate(ctx, data, &i, data, j);
-    if (o)
-        o = EVP_DecryptFinal_ex(ctx, &(data[i]), &j);
+
+    ok = EVP_DecryptInit_ex(ctx, cipher->cipher, NULL, key, &(cipher->iv[0]));
+    if (ok)
+        ok = EVP_DecryptUpdate(ctx, data, &ilen, data, ilen);
+    if (ok) {
+        /* Squirrel away the length of data decrypted so far. */
+        *plen = ilen;
+        ok = EVP_DecryptFinal_ex(ctx, &(data[ilen]), &ilen);
+    }
+    if (ok)
+        *plen += ilen;
+    else
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
+
     EVP_CIPHER_CTX_free(ctx);
     OPENSSL_cleanse((char *)buf, sizeof(buf));
     OPENSSL_cleanse((char *)key, sizeof(key));
-    if (o)
-        j += i;
-    else {
-        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
-        return (0);
-    }
-    *plen = j;
-    return (1);
+    return ok;
 }
 
+/*
+ * This implements a very limited PEM header parser that does not support the
+ * full grammar of rfc1421.  In particular, folded headers are not supported,
+ * nor is additional whitespace.
+ *
+ * A robust implementation would make use of a library that turns the headers
+ * into a BIO from which one folded line is read at a time, and is then split
+ * into a header label and content.  We would then parse the content of the
+ * headers we care about.  This is overkill for just this limited use-case, but
+ * presumably we also parse rfc822-style headers for S/MIME, so a common
+ * abstraction might well be more generally useful.
+ */
 int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher)
 {
+    static const char ProcType[] = "Proc-Type:";
+    static const char ENCRYPTED[] = "ENCRYPTED";
+    static const char DEKInfo[] = "DEK-Info:";
     const EVP_CIPHER *enc = NULL;
+    int ivlen;
     char *dekinfostart, c;
 
     cipher->cipher = NULL;
     if ((header == NULL) || (*header == '\0') || (*header == '\n'))
-        return (1);
-    if (strncmp(header, "Proc-Type: ", 11) != 0) {
+        return 1;
+
+    if (strncmp(header, ProcType, sizeof(ProcType)-1) != 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_PROC_TYPE);
-        return (0);
+        return 0;
     }
-    header += 11;
-    if (*header != '4')
-        return (0);
-    header++;
-    if (*header != ',')
-        return (0);
-    header++;
-    if (strncmp(header, "ENCRYPTED", 9) != 0) {
+    header += sizeof(ProcType)-1;
+    header += strspn(header, " \t");
+
+    if (*header++ != '4' || *header++ != ',')
+        return 0;
+    header += strspn(header, " \t");
+
+    /* We expect "ENCRYPTED" followed by optional white-space + line break */
+    if (strncmp(header, ENCRYPTED, sizeof(ENCRYPTED)-1) != 0 ||
+        strspn(header+sizeof(ENCRYPTED)-1, " \t\r\n") == 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_ENCRYPTED);
-        return (0);
+        return 0;
     }
-    for (; (*header != '\n') && (*header != '\0'); header++) ;
-    if (*header == '\0') {
+    header += sizeof(ENCRYPTED)-1;
+    header += strspn(header, " \t\r");
+    if (*header++ != '\n') {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_SHORT_HEADER);
-        return (0);
+        return 0;
     }
-    header++;
-    if (strncmp(header, "DEK-Info: ", 10) != 0) {
+
+    /*-
+     * https://tools.ietf.org/html/rfc1421#section-4.6.1.3
+     * We expect "DEK-Info: algo[,hex-parameters]"
+     */
+    if (strncmp(header, DEKInfo, sizeof(DEKInfo)-1) != 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_DEK_INFO);
-        return (0);
+        return 0;
     }
-    header += 10;
+    header += sizeof(DEKInfo)-1;
+    header += strspn(header, " \t");
 
+    /*
+     * DEK-INFO is a comma-separated combination of algorithm name and optional
+     * parameters.
+     */
     dekinfostart = header;
-    for (;;) {
-        c = *header;
-#ifndef CHARSET_EBCDIC
-        if (!(((c >= 'A') && (c <= 'Z')) || (c == '-') ||
-              ((c >= '0') && (c <= '9'))))
-            break;
-#else
-        if (!(isupper(c) || (c == '-') || isdigit(c)))
-            break;
-#endif
-        header++;
-    }
+    header += strcspn(header, " \t,");
+    c = *header;
     *header = '\0';
     cipher->cipher = enc = EVP_get_cipherbyname(dekinfostart);
-    *header++ = c;
+    *header = c;
+    header += strspn(header, " \t");
 
     if (enc == NULL) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNSUPPORTED_ENCRYPTION);
-        return (0);
+        return 0;
     }
+    ivlen = EVP_CIPHER_iv_length(enc);
+    if (ivlen > 0 && *header++ != ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_MISSING_DEK_IV);
+        return 0;
+    } else if (ivlen == 0 && *header == ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNEXPECTED_DEK_IV);
+        return 0;
+    }
+
     if (!load_iv(&header, cipher->iv, EVP_CIPHER_iv_length(enc)))
-        return (0);
+        return 0;
 
-    return (1);
+    return 1;
 }
 
 static int load_iv(char **fromp, unsigned char *to, int num)
@@ -577,7 +618,8 @@ int PEM_write_bio(BIO *bp, const char *name, const char *header,
     i = j = 0;
     while (len > 0) {
         n = (int)((len > (PEM_BUFSIZE * 5)) ? (PEM_BUFSIZE * 5) : len);
-        EVP_EncodeUpdate(ctx, buf, &outl, &(data[j]), n);
+        if (!EVP_EncodeUpdate(ctx, buf, &outl, &(data[j]), n))
+            goto err;
         if ((outl) && (BIO_write(bp, (char *)buf, outl) != outl))
             goto err;
         i += outl;