apps/s_socket.c: address rare TLSProxy failures on Windows.
[openssl.git] / apps / opt.c
index 1bd3965b438c10c05277daba3a83b95eb1449dd5..cc1418449e29b1c685be546ae896e75203c2801b 100644 (file)
@@ -1,53 +1,11 @@
-/* ====================================================================
- * Copyright (c) 2015 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    licensing@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+/*
+ * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
  *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
-
-/* #define COMPILE_STANDALONE_TEST_DRIVER  */
 #include "apps.h"
 #include <string.h>
 #if !defined(OPENSSL_SYS_MSDOS)
@@ -59,6 +17,7 @@
 #include <ctype.h>
 #include <limits.h>
 #include <openssl/bio.h>
+#include <openssl/x509v3.h>
 
 #define MAX_OPT_HELP_WIDTH 30
 const char OPT_HELP_STR[] = "--";
@@ -78,7 +37,7 @@ static char prog[40];
 /*
  * Return the simple name of the program; removing various platform gunk.
  */
-#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_NETWARE)
+#if defined(OPENSSL_SYS_WIN32)
 char *opt_progname(const char *argv0)
 {
     size_t i, n;
@@ -97,17 +56,12 @@ char *opt_progname(const char *argv0)
     if (n > 4 &&
         (strcmp(&p[n - 4], ".exe") == 0 || strcmp(&p[n - 4], ".EXE") == 0))
         n -= 4;
-#if defined(OPENSSL_SYS_NETWARE)
-    if (n > 4 &&
-        (strcmp(&p[n - 4], ".nlm") == 0 || strcmp(&p[n - 4], ".NLM") == 0))
-        n -= 4;
-#endif
 
     /* Copy over the name, in lowercase. */
-    if (n > sizeof prog - 1)
-        n = sizeof prog - 1;
+    if (n > sizeof(prog) - 1)
+        n = sizeof(prog) - 1;
     for (q = prog, i = 0; i < n; i++, p++)
-        *q++ = isupper(*p) ? tolower(*p) : *p;
+        *q++ = tolower((unsigned char)*p);
     *q = '\0';
     return prog;
 }
@@ -118,7 +72,7 @@ char *opt_progname(const char *argv0)
 {
     const char *p, *q;
 
-    /* Find last special charcter sys:[foo.bar]openssl */
+    /* Find last special character sys:[foo.bar]openssl */
     for (p = argv0 + strlen(argv0); --p > argv0;)
         if (*p == ':' || *p == ']' || *p == '>') {
             p++;
@@ -126,9 +80,9 @@ char *opt_progname(const char *argv0)
         }
 
     q = strrchr(p, '.');
-    strncpy(prog, p, sizeof prog - 1);
-    prog[sizeof prog - 1] = '\0';
-    if (q != NULL && q - p < sizeof prog)
+    strncpy(prog, p, sizeof(prog) - 1);
+    prog[sizeof(prog) - 1] = '\0';
+    if (q != NULL && q - p < sizeof(prog))
         prog[q - p] = '\0';
     return prog;
 }
@@ -145,8 +99,8 @@ char *opt_progname(const char *argv0)
             p++;
             break;
         }
-    strncpy(prog, p, sizeof prog - 1);
-    prog[sizeof prog - 1] = '\0';
+    strncpy(prog, p, sizeof(prog) - 1);
+    prog[sizeof(prog) - 1] = '\0';
     return prog;
 }
 #endif
@@ -168,8 +122,8 @@ char *opt_init(int ac, char **av, const OPTIONS *o)
     unknown = NULL;
 
     for (; o->name; ++o) {
-        const OPTIONS *next;
 #ifndef NDEBUG
+        const OPTIONS *next;
         int duplicated, i;
 #endif
 
@@ -181,10 +135,14 @@ char *opt_init(int ac, char **av, const OPTIONS *o)
         /* Make sure options are legit. */
         assert(o->name[0] != '-');
         assert(o->retval > 0);
-        assert(i == 0 || i == '-'
-               || i == 'n' || i == 'p' || i == 'u'
-               || i == 's' || i == '<' || i == '>' || i == '/'
-               || i == 'f' || i == 'F');
+        switch (i) {
+        case   0: case '-': case '/': case '<': case '>': case 'E': case 'F':
+        case 'M': case 'U': case 'f': case 'l': case 'n': case 'p': case 's':
+        case 'u': case 'c':
+            break;
+        default:
+            assert(0);
+        }
 
         /* Make sure there are no duplicates. */
         for (next = o + 1; next->name; ++next) {
@@ -223,10 +181,10 @@ int opt_format_error(const char *s, unsigned long flags)
 {
     OPT_PAIR *ap;
 
-    if (flags == OPT_FMT_PEMDER)
+    if (flags == OPT_FMT_PEMDER) {
         BIO_printf(bio_err, "%s: Bad format \"%s\"; must be pem or der\n",
                    prog, s);
-    else {
+    else {
         BIO_printf(bio_err, "%s: Bad format \"%s\"; must be one of:\n",
                    prog, s);
         for (ap = formats; ap->name; ap++)
@@ -306,8 +264,9 @@ int opt_format(const char *s, unsigned long flags, int *result)
             if ((flags & OPT_FMT_PKCS12) == 0)
                 return opt_format_error(s, flags);
             *result = FORMAT_PKCS12;
-        } else
+        } else {
             return 0;
+        }
         break;
     }
     return 1;
@@ -317,9 +276,9 @@ int opt_format(const char *s, unsigned long flags, int *result)
 int opt_cipher(const char *name, const EVP_CIPHER **cipherp)
 {
     *cipherp = EVP_get_cipherbyname(name);
-    if (*cipherp)
+    if (*cipherp != NULL)
         return 1;
-    BIO_printf(bio_err, "%s: Unknown cipher %s\n", prog, name);
+    BIO_printf(bio_err, "%s: Unrecognized flag %s\n", prog, name);
     return 0;
 }
 
@@ -329,9 +288,9 @@ int opt_cipher(const char *name, const EVP_CIPHER **cipherp)
 int opt_md(const char *name, const EVP_MD **mdp)
 {
     *mdp = EVP_get_digestbyname(name);
-    if (*mdp)
+    if (*mdp != NULL)
         return 1;
-    BIO_printf(bio_err, "%s: Unknown digest %s\n", prog, name);
+    BIO_printf(bio_err, "%s: Unrecognized flag %s\n", prog, name);
     return 0;
 }
 
@@ -367,6 +326,30 @@ int opt_int(const char *value, int *result)
     return 1;
 }
 
+static void opt_number_error(const char *v)
+{
+    size_t i = 0;
+    struct strstr_pair_st {
+        char *prefix;
+        char *name;
+    } b[] = {
+        {"0x", "a hexadecimal"},
+        {"0X", "a hexadecimal"},
+        {"0", "an octal"}
+    };
+
+    for (i = 0; i < OSSL_NELEM(b); i++) {
+        if (strncmp(v, b[i].prefix, strlen(b[i].prefix)) == 0) {
+            BIO_printf(bio_err,
+                       "%s: Can't parse \"%s\" as %s number\n",
+                       prog, v, b[i].name);
+            return;
+        }
+    }
+    BIO_printf(bio_err, "%s: Can't parse \"%s\" as a number\n", prog, v);
+    return;
+}
+
 /* Parse a long, put it into *result; return 0 on failure, else 1. */
 int opt_long(const char *value, long *result)
 {
@@ -374,13 +357,13 @@ int opt_long(const char *value, long *result)
     long l;
     char *endp;
 
+    errno = 0;
     l = strtol(value, &endp, 0);
     if (*endp
             || endp == value
             || ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE)
             || (l == 0 && errno != 0)) {
-        BIO_printf(bio_err, "%s: Can't parse \"%s\" as a number\n",
-                   prog, value);
+        opt_number_error(value);
         errno = oerrno;
         return 0;
     }
@@ -389,6 +372,54 @@ int opt_long(const char *value, long *result)
     return 1;
 }
 
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \
+    defined(INTMAX_MAX) && defined(UINTMAX_MAX)
+
+/* Parse an intmax_t, put it into *result; return 0 on failure, else 1. */
+int opt_imax(const char *value, intmax_t *result)
+{
+    int oerrno = errno;
+    intmax_t m;
+    char *endp;
+
+    errno = 0;
+    m = strtoimax(value, &endp, 0);
+    if (*endp
+            || endp == value
+            || ((m == INTMAX_MAX || m == INTMAX_MIN) && errno == ERANGE)
+            || (m == 0 && errno != 0)) {
+        opt_number_error(value);
+        errno = oerrno;
+        return 0;
+    }
+    *result = m;
+    errno = oerrno;
+    return 1;
+}
+
+/* Parse a uintmax_t, put it into *result; return 0 on failure, else 1. */
+int opt_umax(const char *value, uintmax_t *result)
+{
+    int oerrno = errno;
+    uintmax_t m;
+    char *endp;
+
+    errno = 0;
+    m = strtoumax(value, &endp, 0);
+    if (*endp
+            || endp == value
+            || (m == UINTMAX_MAX && errno == ERANGE)
+            || (m == 0 && errno != 0)) {
+        opt_number_error(value);
+        errno = oerrno;
+        return 0;
+    }
+    *result = m;
+    errno = oerrno;
+    return 1;
+}
+#endif
+
 /*
  * Parse an unsigned long, put it into *result; return 0 on failure, else 1.
  */
@@ -398,13 +429,13 @@ int opt_ulong(const char *value, unsigned long *result)
     char *endptr;
     unsigned long l;
 
+    errno = 0;
     l = strtoul(value, &endptr, 0);
     if (*endptr
             || endptr == value
             || ((l == ULONG_MAX) && errno == ERANGE)
             || (l == 0 && errno != 0)) {
-        BIO_printf(bio_err, "%s: Can't parse \"%s\" as an unsigned number\n",
-                   prog, value);
+        opt_number_error(value);
         errno = oerrno;
         return 0;
     }
@@ -422,8 +453,8 @@ enum range { OPT_V_ENUM };
 
 int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
 {
-    long l;
     int i;
+    ossl_intmax_t t = 0;
     ASN1_OBJECT *otmp;
     X509_PURPOSE *xptmp;
     const X509_VERIFY_PARAM *vtmp;
@@ -445,14 +476,25 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
         X509_VERIFY_PARAM_add0_policy(vpm, otmp);
         break;
     case OPT_V_PURPOSE:
+        /* purpose name -> purpose index */
         i = X509_PURPOSE_get_by_sname(opt_arg());
         if (i < 0) {
             BIO_printf(bio_err, "%s: Invalid purpose %s\n", prog, opt_arg());
             return 0;
         }
+
+        /* purpose index -> purpose object */
         xptmp = X509_PURPOSE_get0(i);
+
+        /* purpose object -> purpose value */
         i = X509_PURPOSE_get_id(xptmp);
-        X509_VERIFY_PARAM_set_purpose(vpm, i);
+
+        if (!X509_VERIFY_PARAM_set_purpose(vpm, i)) {
+            BIO_printf(bio_err,
+                       "%s: Internal error setting purpose %s\n",
+                       prog, opt_arg());
+            return 0;
+        }
         break;
     case OPT_V_VERIFY_NAME:
         vtmp = X509_VERIFY_PARAM_lookup(opt_arg());
@@ -468,11 +510,20 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
         if (i >= 0)
             X509_VERIFY_PARAM_set_depth(vpm, i);
         break;
+    case OPT_V_VERIFY_AUTH_LEVEL:
+        i = atoi(opt_arg());
+        if (i >= 0)
+            X509_VERIFY_PARAM_set_auth_level(vpm, i);
+        break;
     case OPT_V_ATTIME:
-        /* If we have C99 we could use intmax_t for all time_t values */
-        opt_long(opt_arg(), &l);
-        if (l)
-            X509_VERIFY_PARAM_set_time(vpm, (time_t)l);
+        if (!opt_imax(opt_arg(), &t))
+            return 0;
+        if (t != (time_t)t) {
+            BIO_printf(bio_err, "%s: epoch time out of range %s\n",
+                       prog, opt_arg());
+            return 0;
+        }
+        X509_VERIFY_PARAM_set_time(vpm, (time_t)t);
         break;
     case OPT_V_VERIFY_HOSTNAME:
         if (!X509_VERIFY_PARAM_set1_host(vpm, opt_arg(), 0))
@@ -490,7 +541,7 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
         X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_IGNORE_CRITICAL);
         break;
     case OPT_V_ISSUER_CHECKS:
-        X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CB_ISSUER_CHECK);
+        /* NOP, deprecated */
         break;
     case OPT_V_CRL_CHECK:
         X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CRL_CHECK);
@@ -544,10 +595,13 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
         break;
     case OPT_V_NO_ALT_CHAINS:
         X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_ALT_CHAINS);
-       break;
+        break;
     case OPT_V_NO_CHECK_TIME:
         X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_CHECK_TIME);
-       break;
+        break;
+    case OPT_V_ALLOW_PROXY_CERTS:
+        X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_ALLOW_PROXY_CERTS);
+        break;
     }
     return 1;
 
@@ -562,7 +616,10 @@ int opt_next(void)
     char *p;
     const OPTIONS *o;
     int ival;
-    unsigned long uval;
+    long lval;
+    unsigned long ulval;
+    ossl_intmax_t imval;
+    ossl_uintmax_t umval;
 
     /* Look at current arg; at end of the list? */
     arg = NULL;
@@ -619,26 +676,16 @@ int opt_next(void)
             /* Just a string. */
             break;
         case '/':
-            if (app_isdir(arg) >= 0)
+            if (app_isdir(arg) > 0)
                 break;
             BIO_printf(bio_err, "%s: Not a directory: %s\n", prog, arg);
             return -1;
         case '<':
             /* Input file. */
-            if (strcmp(arg, "-") == 0 || app_access(arg, R_OK) >= 0)
-                break;
-            BIO_printf(bio_err,
-                       "%s: Cannot open input file %s, %s\n",
-                       prog, arg, strerror(errno));
-            return -1;
+            break;
         case '>':
             /* Output file. */
-            if (strcmp(arg, "-") == 0 || app_access(arg, W_OK) >= 0 || errno == ENOENT)
-                break;
-            BIO_printf(bio_err,
-                       "%s: Cannot open output file %s, %s\n",
-                       prog, arg, strerror(errno));
-            return -1;
+            break;
         case 'p':
         case 'n':
             if (!opt_int(arg, &ival)
@@ -649,17 +696,45 @@ int opt_next(void)
                 return -1;
             }
             break;
+        case 'M':
+            if (!opt_imax(arg, &imval)) {
+                BIO_printf(bio_err,
+                           "%s: Invalid number \"%s\" for -%s\n",
+                           prog, arg, o->name);
+                return -1;
+            }
+            break;
+        case 'U':
+            if (!opt_umax(arg, &umval)) {
+                BIO_printf(bio_err,
+                           "%s: Invalid number \"%s\" for -%s\n",
+                           prog, arg, o->name);
+                return -1;
+            }
+            break;
+        case 'l':
+            if (!opt_long(arg, &lval)) {
+                BIO_printf(bio_err,
+                           "%s: Invalid number \"%s\" for -%s\n",
+                           prog, arg, o->name);
+                return -1;
+            }
+            break;
         case 'u':
-            if (!opt_ulong(arg, &uval)) {
+            if (!opt_ulong(arg, &ulval)) {
                 BIO_printf(bio_err,
                            "%s: Invalid number \"%s\" for -%s\n",
                            prog, arg, o->name);
                 return -1;
             }
             break;
-        case 'f':
+        case 'c':
+        case 'E':
         case 'F':
+        case 'f':
             if (opt_format(arg,
+                           o->valtype == 'c' ? OPT_FMT_PDS :
+                           o->valtype == 'E' ? OPT_FMT_PDE :
                            o->valtype == 'F' ? OPT_FMT_PEMDER
                            : OPT_FMT_ANY, &ival))
                 break;
@@ -719,6 +794,7 @@ int opt_num_rest(void)
 static const char *valtype2param(const OPTIONS *o)
 {
     switch (o->valtype) {
+    case 0:
     case '-':
         return "";
     case 's':
@@ -730,15 +806,23 @@ static const char *valtype2param(const OPTIONS *o)
     case '>':
         return "outfile";
     case 'p':
-        return "pnum";
+        return "+int";
     case 'n':
-        return "num";
+        return "int";
+    case 'l':
+        return "long";
     case 'u':
-        return "unum";
+        return "ulong";
+    case 'E':
+        return "PEM|DER|ENGINE";
     case 'F':
-        return "der/pem";
+        return "PEM|DER";
     case 'f':
         return "format";
+    case 'M':
+        return "intmax";
+    case 'U':
+        return "uintmax";
     }
     return "parm";
 }
@@ -765,7 +849,7 @@ void opt_help(const OPTIONS *list)
             i += 1 + strlen(valtype2param(o));
         if (i < MAX_OPT_HELP_WIDTH && i > width)
             width = i;
-        assert(i < (int)sizeof start);
+        assert(i < (int)sizeof(start));
     }
 
     if (standard_prolog)
@@ -782,10 +866,10 @@ void opt_help(const OPTIONS *list)
 
         /* Pad out prefix */
         memset(start, ' ', sizeof(start) - 1);
-        start[sizeof start - 1] = '\0';
+        start[sizeof(start) - 1] = '\0';
 
         if (o->name == OPT_MORE_STR) {
-            /* Continuation of previous line; padd and print. */
+            /* Continuation of previous line; pad and print. */
             start[width] = '\0';
             BIO_printf(bio_err, "%s  %s\n", start, help);
             continue;
@@ -813,90 +897,3 @@ void opt_help(const OPTIONS *list)
         BIO_printf(bio_err, "%s  %s\n", start, help);
     }
 }
-
-#ifdef COMPILE_STANDALONE_TEST_DRIVER
-# include <sys/stat.h>
-
-typedef enum OPTION_choice {
-    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_IN, OPT_INFORM, OPT_OUT, OPT_COUNT, OPT_U, OPT_FLAG,
-    OPT_STR, OPT_NOTUSED
-} OPTION_CHOICE;
-
-static OPTIONS options[] = {
-    {OPT_HELP_STR, 1, '-', "Usage: %s flags\n"},
-    {OPT_HELP_STR, 1, '-', "Valid options are:\n"},
-    {"help", OPT_HELP, '-', "Display this summary"},
-    {"in", OPT_IN, '<', "input file"},
-    {OPT_MORE_STR, 1, '-', "more detail about input"},
-    {"inform", OPT_INFORM, 'f', "input file format; defaults to pem"},
-    {"out", OPT_OUT, '>', "output file"},
-    {"count", OPT_COUNT, 'p', "a counter greater than zero"},
-    {"u", OPT_U, 'u', "an unsigned number"},
-    {"flag", OPT_FLAG, 0, "just some flag"},
-    {"str", OPT_STR, 's', "the magic word"},
-    {"areallyverylongoption", OPT_HELP, '-', "long way for help"},
-    {NULL}
-};
-
-BIO *bio_err;
-
-int app_isdir(const char *name)
-{
-    struct stat sb;
-
-    return name != NULL && stat(name, &sb) >= 0 && S_ISDIR(sb.st_mode);
-}
-
-int main(int ac, char **av)
-{
-    OPTION_CHOICE o;
-    char **rest;
-    char *prog;
-
-    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
-
-    prog = opt_init(ac, av, options);
-    while ((o = opt_next()) != OPT_EOF) {
-        switch (c) {
-        case OPT_NOTUSED:
-        case OPT_EOF:
-        case OPT_ERR:
-            printf("%s: Usage error; try -help.\n", prog);
-            return 1;
-        case OPT_HELP:
-            opt_help(options);
-            return 0;
-        case OPT_IN:
-            printf("in %s\n", opt_arg());
-            break;
-        case OPT_INFORM:
-            printf("inform %s\n", opt_arg());
-            break;
-        case OPT_OUT:
-            printf("out %s\n", opt_arg());
-            break;
-        case OPT_COUNT:
-            printf("count %s\n", opt_arg());
-            break;
-        case OPT_U:
-            printf("u %s\n", opt_arg());
-            break;
-        case OPT_FLAG:
-            printf("flag\n");
-            break;
-        case OPT_STR:
-            printf("str %s\n", opt_arg());
-            break;
-        }
-    }
-    argc = opt_num_rest();
-    argv = opt_rest();
-
-    printf("args = %d\n", argc);
-    if (argc)
-        while (*argv)
-            printf("  %s\n", *argv++);
-    return 0;
-}
-#endif