Version bump
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!{- $config{hashbangperl} -}
|
||||
#!{- $config{HASHBANGPERL} -}
|
||||
# Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the OpenSSL license (the "License"). You may not use
|
||||
|
||||
+137
@@ -25,6 +25,12 @@
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#ifdef __VMS
|
||||
# include <descrip.h>
|
||||
# include <iledef.h>
|
||||
# include <fscndef.h>
|
||||
# include <starlet.h>
|
||||
#endif
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
@@ -1532,12 +1538,27 @@ CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr)
|
||||
BIO *in;
|
||||
CONF *dbattr_conf = NULL;
|
||||
char buf[BSIZE];
|
||||
#ifndef OPENSSL_NO_POSIX_IO
|
||||
FILE *dbfp;
|
||||
struct stat dbst;
|
||||
#endif
|
||||
|
||||
in = BIO_new_file(dbfile, "r");
|
||||
if (in == NULL) {
|
||||
ERR_print_errors(bio_err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_POSIX_IO
|
||||
BIO_get_fp(in, &dbfp);
|
||||
if (fstat(fileno(dbfp), &dbst) == -1) {
|
||||
SYSerr(SYS_F_FSTAT, errno);
|
||||
ERR_add_error_data(3, "fstat('", dbfile, "')");
|
||||
ERR_print_errors(bio_err);
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((tmpdb = TXT_DB_read(in, DB_NUMBER)) == NULL)
|
||||
goto err;
|
||||
|
||||
@@ -1564,6 +1585,11 @@ CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr)
|
||||
}
|
||||
}
|
||||
|
||||
retdb->dbfname = OPENSSL_strdup(dbfile);
|
||||
#ifndef OPENSSL_NO_POSIX_IO
|
||||
retdb->dbst = dbst;
|
||||
#endif
|
||||
|
||||
err:
|
||||
NCONF_free(dbattr_conf);
|
||||
TXT_DB_free(tmpdb);
|
||||
@@ -1709,6 +1735,7 @@ void free_index(CA_DB *db)
|
||||
{
|
||||
if (db) {
|
||||
TXT_DB_free(db->db);
|
||||
OPENSSL_free(db->dbfname);
|
||||
OPENSSL_free(db);
|
||||
}
|
||||
}
|
||||
@@ -2340,6 +2367,116 @@ int app_isdir(const char *name)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* app_dirname section */
|
||||
|
||||
/*
|
||||
* This exactly follows what POSIX's
|
||||
* dirname does, but is implemented
|
||||
* in a more platform independent way.
|
||||
*
|
||||
* path dirname
|
||||
* /usr/lib /usr
|
||||
* /usr/ /
|
||||
* usr .
|
||||
* / /
|
||||
* . .
|
||||
* .. .
|
||||
* "" .
|
||||
*
|
||||
* Note: this function also keeps the
|
||||
* possibility of modifying the 'path'
|
||||
* string same as POSIX dirname.
|
||||
*/
|
||||
static char *posix_dirname(char *path)
|
||||
{
|
||||
size_t l;
|
||||
char *ret = ".";
|
||||
|
||||
l = strlen(path);
|
||||
if (l == 0)
|
||||
goto out;
|
||||
if (strcmp(path, ".") == 0)
|
||||
goto out;
|
||||
if (strcmp(path, "..") == 0)
|
||||
goto out;
|
||||
if (strcmp(path, "/") == 0) {
|
||||
ret = "/";
|
||||
goto out;
|
||||
}
|
||||
if (path[l - 1] == '/') {
|
||||
/* /usr/ */
|
||||
path[l - 1] = '\0';
|
||||
}
|
||||
if ((ret = strrchr(path, '/')) == NULL) {
|
||||
/* usr */
|
||||
ret = ".";
|
||||
} else if (ret == path) {
|
||||
/* /usr */
|
||||
*++ret = '\0';
|
||||
ret = path;
|
||||
} else {
|
||||
/* /usr/lib */
|
||||
*ret = '\0';
|
||||
ret = path;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: implement app_dirname for Windows.
|
||||
*/
|
||||
#if !defined(_WIN32)
|
||||
char *app_dirname(char *path)
|
||||
{
|
||||
return posix_dirname(path);
|
||||
}
|
||||
#elif defined(__VMS)
|
||||
/*
|
||||
* sys$filescan fills the given item list with pointers into the original
|
||||
* path string, so all we need to do is to find the file name and simply
|
||||
* put a NUL byte wherever the FSCN$_NAME pointer points. If there is no
|
||||
* file name part and the path string isn't the empty string, we know for
|
||||
* a fact that the whole string is a directory spec and return it as is.
|
||||
* Otherwise or if that pointer is the starting address of the original
|
||||
* path string, we know to return "sys$disk:[]", which corresponds to the
|
||||
* Unixly ".".
|
||||
*
|
||||
* If sys$filescan returns an error status, we know that this is not
|
||||
* parsable as a VMS file spec, and then use the fallback, in case we
|
||||
* have a Unix type path.
|
||||
*/
|
||||
char *app_dirname(char *path)
|
||||
{
|
||||
char *ret = "sys$disk:[]";
|
||||
struct dsc$descriptor_s dsc_path = { 0 };
|
||||
ile2 itemlist[] = {
|
||||
{0, FSCN$_NAME, 0},
|
||||
{0, 0, 0}
|
||||
};
|
||||
int fields;
|
||||
int status;
|
||||
|
||||
dsc_path.dsc$a_pointer = path;
|
||||
dsc_path.dsc$w_length = strlen(path);
|
||||
status = sys$filescan(&dsc_path, itemlist, &fields, 0, 0);
|
||||
|
||||
if (!(status & 1))
|
||||
return posix_dirname(path);
|
||||
|
||||
if ((fields & (1 << FSCN$_NAME)) == 0) {
|
||||
if (dsc_path.dsc$w_length != 0)
|
||||
ret = path;
|
||||
} else if (itemlist[0].ile2$ps_bufaddr != path) {
|
||||
if (itemlist[0].ile2$ps_bufaddr != path) {
|
||||
*itemlist[0].ile2$ps_bufaddr = '\0';
|
||||
ret = path;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* raw_read|write section */
|
||||
#if defined(__VMS)
|
||||
# include "vms_term_sock.h"
|
||||
|
||||
+11
@@ -14,6 +14,12 @@
|
||||
# include "internal/nelem.h"
|
||||
# include <assert.h>
|
||||
|
||||
# include <sys/types.h>
|
||||
# ifndef OPENSSL_NO_POSIX_IO
|
||||
# include <sys/stat.h>
|
||||
# include <fcntl.h>
|
||||
# endif
|
||||
|
||||
# include <openssl/e_os2.h>
|
||||
# include <openssl/ossl_typ.h>
|
||||
# include <openssl/bio.h>
|
||||
@@ -509,6 +515,10 @@ typedef struct db_attr_st {
|
||||
typedef struct ca_db_st {
|
||||
DB_ATTR attributes;
|
||||
TXT_DB *db;
|
||||
char *dbfname;
|
||||
# ifndef OPENSSL_NO_POSIX_IO
|
||||
struct stat dbst;
|
||||
# endif
|
||||
} CA_DB;
|
||||
|
||||
void* app_malloc(int sz, const char *what);
|
||||
@@ -594,6 +604,7 @@ void store_setup_crl_download(X509_STORE *st);
|
||||
|
||||
int app_isdir(const char *);
|
||||
int app_access(const char *, int flag);
|
||||
char *app_dirname(char *path);
|
||||
int fileno_stdin(void);
|
||||
int fileno_stdout(void);
|
||||
int raw_read_stdin(void *, int);
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ const OPTIONS genrsa_options[] = {
|
||||
{"3", OPT_3, '-', "Use 3 for the E value"},
|
||||
{"F4", OPT_F4, '-', "Use F4 (0x10001) for the E value"},
|
||||
{"f4", OPT_F4, '-', "Use F4 (0x10001) for the E value"},
|
||||
{"out", OPT_OUT, 's', "Output the key to specified file"},
|
||||
{"out", OPT_OUT, '>', "Output the key to specified file"},
|
||||
OPT_R_OPTIONS,
|
||||
{"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
|
||||
{"", OPT_CIPHER, '-', "Encrypt the output with any supported cipher"},
|
||||
|
||||
+322
-55
@@ -26,6 +26,7 @@ NON_EMPTY_TRANSLATION_UNIT
|
||||
/* Needs to be included before the openssl headers */
|
||||
# include "apps.h"
|
||||
# include "progs.h"
|
||||
# include "internal/sockets.h"
|
||||
# include <openssl/e_os2.h>
|
||||
# include <openssl/crypto.h>
|
||||
# include <openssl/err.h>
|
||||
@@ -33,6 +34,23 @@ NON_EMPTY_TRANSLATION_UNIT
|
||||
# include <openssl/evp.h>
|
||||
# include <openssl/bn.h>
|
||||
# include <openssl/x509v3.h>
|
||||
# include <openssl/rand.h>
|
||||
|
||||
# if defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_NO_SOCK)
|
||||
# define OCSP_DAEMON
|
||||
# include <sys/types.h>
|
||||
# include <sys/wait.h>
|
||||
# include <syslog.h>
|
||||
# include <signal.h>
|
||||
# define MAXERRLEN 1000 /* limit error text sent to syslog to 1000 bytes */
|
||||
# else
|
||||
# undef LOG_INFO
|
||||
# undef LOG_WARNING
|
||||
# undef LOG_ERR
|
||||
# define LOG_INFO 0
|
||||
# define LOG_WARNING 1
|
||||
# define LOG_ERR 2
|
||||
# endif
|
||||
|
||||
/* Maximum leeway in validity period: default 5 minutes */
|
||||
# define MAX_VALIDITY_PERIOD (5 * 60)
|
||||
@@ -56,8 +74,19 @@ static void make_ocsp_response(BIO *err, OCSP_RESPONSE **resp, OCSP_REQUEST *req
|
||||
|
||||
static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser);
|
||||
static BIO *init_responder(const char *port);
|
||||
static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio);
|
||||
static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, int timeout);
|
||||
static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp);
|
||||
static void log_message(int level, const char *fmt, ...);
|
||||
static char *prog;
|
||||
static int multi = 0;
|
||||
|
||||
# ifdef OCSP_DAEMON
|
||||
static int acfd = (int) INVALID_SOCKET;
|
||||
static int index_changed(CA_DB *);
|
||||
static void spawn_loop(void);
|
||||
static int print_syslog(const char *str, size_t len, void *levPtr);
|
||||
static void sock_timeout(int signum);
|
||||
# endif
|
||||
|
||||
# ifndef OPENSSL_NO_SOCK
|
||||
static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host,
|
||||
@@ -81,7 +110,8 @@ typedef enum OPTION_choice {
|
||||
OPT_INDEX, OPT_CA, OPT_NMIN, OPT_REQUEST, OPT_NDAYS, OPT_RSIGNER,
|
||||
OPT_RKEY, OPT_ROTHER, OPT_RMD, OPT_RSIGOPT, OPT_HEADER,
|
||||
OPT_V_ENUM,
|
||||
OPT_MD
|
||||
OPT_MD,
|
||||
OPT_MULTI
|
||||
} OPTION_CHOICE;
|
||||
|
||||
const OPTIONS ocsp_options[] = {
|
||||
@@ -101,6 +131,9 @@ const OPTIONS ocsp_options[] = {
|
||||
"Don't include any certificates in response"},
|
||||
{"resp_key_id", OPT_RESP_KEY_ID, '-',
|
||||
"Identify response by signing certificate key ID"},
|
||||
# ifdef OCSP_DAEMON
|
||||
{"multi", OPT_MULTI, 'p', "run multiple responder processes"},
|
||||
# endif
|
||||
{"no_certs", OPT_NO_CERTS, '-',
|
||||
"Don't include any certificates in signed request"},
|
||||
{"no_signature_verify", OPT_NO_SIGNATURE_VERIFY, '-',
|
||||
@@ -197,13 +230,10 @@ int ocsp_main(int argc, char **argv)
|
||||
int accept_count = -1, add_nonce = 1, noverify = 0, use_ssl = -1;
|
||||
int vpmtouched = 0, badsig = 0, i, ignore_err = 0, nmin = 0, ndays = -1;
|
||||
int req_text = 0, resp_text = 0, ret = 1;
|
||||
#ifndef OPENSSL_NO_SOCK
|
||||
int req_timeout = -1;
|
||||
#endif
|
||||
long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
|
||||
unsigned long sign_flags = 0, verify_flags = 0, rflags = 0;
|
||||
OPTION_CHOICE o;
|
||||
char *prog;
|
||||
|
||||
reqnames = sk_OPENSSL_STRING_new_null();
|
||||
if (reqnames == NULL)
|
||||
@@ -451,9 +481,13 @@ int ocsp_main(int argc, char **argv)
|
||||
goto opthelp;
|
||||
trailing_md = 1;
|
||||
break;
|
||||
case OPT_MULTI:
|
||||
# ifdef OCSP_DAEMON
|
||||
multi = atoi(opt_arg());
|
||||
# endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (trailing_md) {
|
||||
BIO_printf(bio_err, "%s: Digest must be before -cert or -serial\n",
|
||||
prog);
|
||||
@@ -464,7 +498,7 @@ int ocsp_main(int argc, char **argv)
|
||||
goto opthelp;
|
||||
|
||||
/* Have we anything to do? */
|
||||
if (req == NULL&& reqin == NULL
|
||||
if (req == NULL && reqin == NULL
|
||||
&& respin == NULL && !(port != NULL && ridx_filename != NULL))
|
||||
goto opthelp;
|
||||
|
||||
@@ -514,14 +548,53 @@ int ocsp_main(int argc, char **argv)
|
||||
if (rkey == NULL)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ridx_filename != NULL
|
||||
&& (rkey != NULL || rsigner != NULL || rca_cert != NULL)) {
|
||||
BIO_printf(bio_err,
|
||||
"Responder mode requires certificate, key, and CA.\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ridx_filename != NULL) {
|
||||
rdb = load_index(ridx_filename, NULL);
|
||||
if (rdb == NULL || !index_index(rdb)) {
|
||||
ret = 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
# ifdef OCSP_DAEMON
|
||||
if (multi && acbio != NULL)
|
||||
spawn_loop();
|
||||
if (acbio != NULL && req_timeout > 0)
|
||||
signal(SIGALRM, sock_timeout);
|
||||
#endif
|
||||
|
||||
if (acbio != NULL)
|
||||
BIO_printf(bio_err, "Waiting for OCSP client connections...\n");
|
||||
log_message(LOG_INFO, "waiting for OCSP client connections...");
|
||||
|
||||
redo_accept:
|
||||
|
||||
if (acbio != NULL) {
|
||||
if (!do_responder(&req, &cbio, acbio))
|
||||
goto end;
|
||||
# ifdef OCSP_DAEMON
|
||||
if (index_changed(rdb)) {
|
||||
CA_DB *newrdb = load_index(ridx_filename, NULL);
|
||||
|
||||
if (newrdb != NULL) {
|
||||
free_index(rdb);
|
||||
rdb = newrdb;
|
||||
} else {
|
||||
log_message(LOG_ERR, "error reloading updated index: %s",
|
||||
ridx_filename);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
req = NULL;
|
||||
if (!do_responder(&req, &cbio, acbio, req_timeout))
|
||||
goto redo_accept;
|
||||
|
||||
if (req == NULL) {
|
||||
resp =
|
||||
OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST,
|
||||
@@ -577,21 +650,6 @@ redo_accept:
|
||||
BIO_free(derbio);
|
||||
}
|
||||
|
||||
if (ridx_filename != NULL
|
||||
&& (rkey == NULL || rsigner == NULL || rca_cert == NULL)) {
|
||||
BIO_printf(bio_err,
|
||||
"Need a responder certificate, key and CA for this operation!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ridx_filename != NULL && rdb == NULL) {
|
||||
rdb = load_index(ridx_filename, NULL);
|
||||
if (rdb == NULL)
|
||||
goto end;
|
||||
if (!index_index(rdb))
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (rdb != NULL) {
|
||||
make_ocsp_response(bio_err, &resp, req, rdb, rca_cert, rsigner, rkey,
|
||||
rsign_md, rsign_sigopts, rother, rflags, nmin, ndays, badsig);
|
||||
@@ -637,10 +695,10 @@ redo_accept:
|
||||
if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
|
||||
BIO_printf(out, "Responder Error: %s (%d)\n",
|
||||
OCSP_response_status_str(i), i);
|
||||
if (ignore_err)
|
||||
goto redo_accept;
|
||||
ret = 0;
|
||||
goto end;
|
||||
if (!ignore_err) {
|
||||
ret = 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp_text)
|
||||
@@ -746,6 +804,180 @@ redo_accept:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
log_message(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
# ifdef OCSP_DAEMON
|
||||
if (multi) {
|
||||
vsyslog(level, fmt, ap);
|
||||
if (level >= LOG_ERR)
|
||||
ERR_print_errors_cb(print_syslog, &level);
|
||||
}
|
||||
# endif
|
||||
if (!multi) {
|
||||
BIO_printf(bio_err, "%s: ", prog);
|
||||
BIO_vprintf(bio_err, fmt, ap);
|
||||
BIO_printf(bio_err, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
# ifdef OCSP_DAEMON
|
||||
|
||||
static int print_syslog(const char *str, size_t len, void *levPtr)
|
||||
{
|
||||
int level = *(int *)levPtr;
|
||||
int ilen = (len > MAXERRLEN) ? MAXERRLEN : len;
|
||||
|
||||
syslog(level, "%.*s", ilen, str);
|
||||
|
||||
return ilen;
|
||||
}
|
||||
|
||||
static int index_changed(CA_DB *rdb)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (rdb != NULL && stat(rdb->dbfname, &sb) != -1) {
|
||||
if (rdb->dbst.st_mtime != sb.st_mtime
|
||||
|| rdb->dbst.st_ctime != sb.st_ctime
|
||||
|| rdb->dbst.st_ino != sb.st_ino
|
||||
|| rdb->dbst.st_dev != sb.st_dev) {
|
||||
syslog(LOG_INFO, "index file changed, reloading");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void killall(int ret, pid_t *kidpids)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < multi; ++i)
|
||||
if (kidpids[i] != 0)
|
||||
(void)kill(kidpids[i], SIGTERM);
|
||||
sleep(1);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
static int termsig = 0;
|
||||
|
||||
static void noteterm (int sig)
|
||||
{
|
||||
termsig = sig;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop spawning up to `multi` child processes, only child processes return
|
||||
* from this function. The parent process loops until receiving a termination
|
||||
* signal, kills extant children and exits without returning.
|
||||
*/
|
||||
static void spawn_loop(void)
|
||||
{
|
||||
const char *signame;
|
||||
pid_t *kidpids = NULL;
|
||||
int status;
|
||||
int procs = 0;
|
||||
int i;
|
||||
|
||||
openlog(prog, LOG_PID, LOG_DAEMON);
|
||||
|
||||
if (setpgid(0, 0)) {
|
||||
syslog(LOG_ERR, "fatal: error detaching from parent process group: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
kidpids = app_malloc(multi * sizeof(*kidpids), "child PID array");
|
||||
for (i = 0; i < multi; ++i)
|
||||
kidpids[i] = 0;
|
||||
|
||||
signal(SIGINT, noteterm);
|
||||
signal(SIGTERM, noteterm);
|
||||
|
||||
while (termsig == 0) {
|
||||
pid_t fpid;
|
||||
|
||||
/*
|
||||
* Wait for a child to replace when we're at the limit.
|
||||
* Slow down if a child exited abnormally or waitpid() < 0
|
||||
*/
|
||||
while (termsig == 0 && procs >= multi) {
|
||||
if ((fpid = waitpid(-1, &status, 0)) > 0) {
|
||||
for (i = 0; i < procs; ++i) {
|
||||
if (kidpids[i] == fpid) {
|
||||
kidpids[i] = 0;
|
||||
--procs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= multi) {
|
||||
syslog(LOG_ERR, "fatal: internal error: "
|
||||
"no matching child slot for pid: %ld",
|
||||
(long) fpid);
|
||||
killall(1, kidpids);
|
||||
}
|
||||
if (status != 0) {
|
||||
if (WIFEXITED(status))
|
||||
syslog(LOG_WARNING, "child process: %ld, exit status: %d",
|
||||
(long)fpid, WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status))
|
||||
syslog(LOG_WARNING, "child process: %ld, term signal %d%s",
|
||||
(long)fpid, WTERMSIG(status),
|
||||
WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
sleep(1);
|
||||
}
|
||||
break;
|
||||
} else if (errno != EINTR) {
|
||||
syslog(LOG_ERR, "fatal: waitpid(): %s", strerror(errno));
|
||||
killall(1, kidpids);
|
||||
}
|
||||
}
|
||||
if (termsig)
|
||||
break;
|
||||
|
||||
switch(fpid = fork()) {
|
||||
case -1: /* error */
|
||||
/* System critically low on memory, pause and try again later */
|
||||
sleep(30);
|
||||
break;
|
||||
case 0: /* child */
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
if (termsig)
|
||||
_exit(0);
|
||||
if (RAND_poll() <= 0) {
|
||||
syslog(LOG_ERR, "fatal: RAND_poll() failed");
|
||||
_exit(1);
|
||||
}
|
||||
return;
|
||||
default: /* parent */
|
||||
for (i = 0; i < multi; ++i) {
|
||||
if (kidpids[i] == 0) {
|
||||
kidpids[i] = fpid;
|
||||
procs++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= multi) {
|
||||
syslog(LOG_ERR, "fatal: internal error: no free child slots");
|
||||
killall(1, kidpids);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* The loop above can only break on termsig */
|
||||
signame = strsignal(termsig);
|
||||
syslog(LOG_INFO, "terminating on signal: %s(%d)",
|
||||
signame ? signame : "", termsig);
|
||||
killall(0, kidpids);
|
||||
}
|
||||
# endif
|
||||
|
||||
static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert,
|
||||
const EVP_MD *cert_id_md, X509 *issuer,
|
||||
STACK_OF(OCSP_CERTID) *ids)
|
||||
@@ -1035,16 +1267,14 @@ static BIO *init_responder(const char *port)
|
||||
if (acbio == NULL
|
||||
|| BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) < 0
|
||||
|| BIO_set_accept_port(acbio, port) < 0) {
|
||||
BIO_printf(bio_err, "Error setting up accept BIO\n");
|
||||
ERR_print_errors(bio_err);
|
||||
log_message(LOG_ERR, "Error setting up accept BIO");
|
||||
goto err;
|
||||
}
|
||||
|
||||
BIO_set_accept_bios(acbio, bufbio);
|
||||
bufbio = NULL;
|
||||
if (BIO_do_accept(acbio) <= 0) {
|
||||
BIO_printf(bio_err, "Error starting accept\n");
|
||||
ERR_print_errors(bio_err);
|
||||
log_message(LOG_ERR, "Error starting accept");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -1083,7 +1313,16 @@ static int urldecode(char *p)
|
||||
}
|
||||
# endif
|
||||
|
||||
static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio)
|
||||
# ifdef OCSP_DAEMON
|
||||
static void sock_timeout(int signum)
|
||||
{
|
||||
if (acfd != (int)INVALID_SOCKET)
|
||||
(void)shutdown(acfd, SHUT_RD);
|
||||
}
|
||||
# endif
|
||||
|
||||
static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio,
|
||||
int timeout)
|
||||
{
|
||||
# ifdef OPENSSL_NO_SOCK
|
||||
return 0;
|
||||
@@ -1093,27 +1332,37 @@ static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio)
|
||||
char inbuf[2048], reqbuf[2048];
|
||||
char *p, *q;
|
||||
BIO *cbio = NULL, *getbio = NULL, *b64 = NULL;
|
||||
const char *client;
|
||||
|
||||
if (BIO_do_accept(acbio) <= 0) {
|
||||
BIO_printf(bio_err, "Error accepting connection\n");
|
||||
ERR_print_errors(bio_err);
|
||||
*preq = NULL;
|
||||
|
||||
/* Connection loss before accept() is routine, ignore silently */
|
||||
if (BIO_do_accept(acbio) <= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
cbio = BIO_pop(acbio);
|
||||
*pcbio = cbio;
|
||||
client = BIO_get_peer_name(cbio);
|
||||
|
||||
# ifdef OCSP_DAEMON
|
||||
if (timeout > 0) {
|
||||
(void) BIO_get_fd(cbio, &acfd);
|
||||
alarm(timeout);
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Read the request line. */
|
||||
len = BIO_gets(cbio, reqbuf, sizeof(reqbuf));
|
||||
if (len <= 0)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
if (strncmp(reqbuf, "GET ", 4) == 0) {
|
||||
/* Expecting GET {sp} /URL {sp} HTTP/1.x */
|
||||
for (p = reqbuf + 4; *p == ' '; ++p)
|
||||
continue;
|
||||
if (*p != '/') {
|
||||
BIO_printf(bio_err, "Invalid request -- bad URL\n");
|
||||
return 1;
|
||||
log_message(LOG_INFO, "Invalid request -- bad URL: %s", client);
|
||||
goto out;
|
||||
}
|
||||
p++;
|
||||
|
||||
@@ -1122,37 +1371,51 @@ static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio)
|
||||
if (*q == ' ')
|
||||
break;
|
||||
if (strncmp(q, " HTTP/1.", 8) != 0) {
|
||||
BIO_printf(bio_err, "Invalid request -- bad HTTP version\n");
|
||||
return 1;
|
||||
log_message(LOG_INFO,
|
||||
"Invalid request -- bad HTTP version: %s", client);
|
||||
goto out;
|
||||
}
|
||||
*q = '\0';
|
||||
|
||||
/*
|
||||
* Skip "GET / HTTP..." requests often used by load-balancers
|
||||
*/
|
||||
if (p[1] == '\0')
|
||||
goto out;
|
||||
|
||||
len = urldecode(p);
|
||||
if (len <= 0) {
|
||||
BIO_printf(bio_err, "Invalid request -- bad URL encoding\n");
|
||||
return 1;
|
||||
log_message(LOG_INFO,
|
||||
"Invalid request -- bad URL encoding: %s", client);
|
||||
goto out;
|
||||
}
|
||||
if ((getbio = BIO_new_mem_buf(p, len)) == NULL
|
||||
|| (b64 = BIO_new(BIO_f_base64())) == NULL) {
|
||||
BIO_printf(bio_err, "Could not allocate memory\n");
|
||||
ERR_print_errors(bio_err);
|
||||
return 1;
|
||||
log_message(LOG_ERR, "Could not allocate base64 bio: %s", client);
|
||||
goto out;
|
||||
}
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
getbio = BIO_push(b64, getbio);
|
||||
} else if (strncmp(reqbuf, "POST ", 5) != 0) {
|
||||
BIO_printf(bio_err, "Invalid request -- bad HTTP verb\n");
|
||||
return 1;
|
||||
log_message(LOG_INFO, "Invalid request -- bad HTTP verb: %s", client);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read and skip past the headers. */
|
||||
for (;;) {
|
||||
len = BIO_gets(cbio, inbuf, sizeof(inbuf));
|
||||
if (len <= 0)
|
||||
return 1;
|
||||
goto out;
|
||||
if ((inbuf[0] == '\r') || (inbuf[0] == '\n'))
|
||||
break;
|
||||
}
|
||||
|
||||
# ifdef OCSP_DAEMON
|
||||
/* Clear alarm before we close the client socket */
|
||||
alarm(0);
|
||||
timeout = 0;
|
||||
# endif
|
||||
|
||||
/* Try to read OCSP request */
|
||||
if (getbio != NULL) {
|
||||
req = d2i_OCSP_REQUEST_bio(getbio, NULL);
|
||||
@@ -1161,13 +1424,17 @@ static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio)
|
||||
req = d2i_OCSP_REQUEST_bio(cbio, NULL);
|
||||
}
|
||||
|
||||
if (req == NULL) {
|
||||
BIO_printf(bio_err, "Error parsing OCSP request\n");
|
||||
ERR_print_errors(bio_err);
|
||||
}
|
||||
if (req == NULL)
|
||||
log_message(LOG_ERR, "Error parsing OCSP request");
|
||||
|
||||
*preq = req;
|
||||
|
||||
out:
|
||||
# ifdef OCSP_DAEMON
|
||||
if (timeout > 0)
|
||||
alarm(0);
|
||||
acfd = (int)INVALID_SOCKET;
|
||||
# endif
|
||||
return 1;
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
# This is mostly being used for generation of certificate requests.
|
||||
#
|
||||
|
||||
# Note that you can include other files from the main configuration
|
||||
# file using the .include directive.
|
||||
#.include filename
|
||||
|
||||
# This definition stops the following lines choking if HOME isn't
|
||||
# defined.
|
||||
HOME = .
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
# This is mostly being used for generation of certificate requests.
|
||||
#
|
||||
|
||||
# Note that you can include other files from the main configuration
|
||||
# file using the .include directive.
|
||||
#.include filename
|
||||
|
||||
# This definition stops the following lines choking if HOME isn't
|
||||
# defined.
|
||||
HOME = .
|
||||
|
||||
+36
-5
@@ -613,13 +613,17 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
|
||||
*/
|
||||
int opt_next(void)
|
||||
{
|
||||
char *p;
|
||||
char *p, *estr;
|
||||
const OPTIONS *o;
|
||||
int ival;
|
||||
long lval;
|
||||
unsigned long ulval;
|
||||
ossl_intmax_t imval;
|
||||
ossl_uintmax_t umval;
|
||||
#if !defined(_WIN32)
|
||||
char *c;
|
||||
int oerrno;
|
||||
#endif
|
||||
|
||||
/* Look at current arg; at end of the list? */
|
||||
arg = NULL;
|
||||
@@ -676,13 +680,13 @@ 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)
|
||||
if (strcmp(arg, "-") == 0 || app_access(arg, R_OK) == 0)
|
||||
break;
|
||||
BIO_printf(bio_err,
|
||||
"%s: Cannot open input file %s, %s\n",
|
||||
@@ -690,11 +694,38 @@ int opt_next(void)
|
||||
return -1;
|
||||
case '>':
|
||||
/* Output file. */
|
||||
if (strcmp(arg, "-") == 0 || app_access(arg, W_OK) >= 0 || errno == ENOENT)
|
||||
#if !defined(_WIN32)
|
||||
c = OPENSSL_strdup(arg);
|
||||
if (c == NULL) {
|
||||
BIO_printf(bio_err,
|
||||
"%s: Memory allocation failure\n", prog);
|
||||
return -1;
|
||||
}
|
||||
oerrno = errno;
|
||||
errno = 0;
|
||||
if (strcmp(arg, "-") == 0
|
||||
|| (app_access(app_dirname(c), W_OK) == 0
|
||||
&& app_isdir(arg) <= 0
|
||||
&& (app_access(arg, W_OK) == 0 || errno == ENOENT))) {
|
||||
OPENSSL_free(c);
|
||||
break;
|
||||
}
|
||||
OPENSSL_free(c);
|
||||
if (errno == 0)
|
||||
/* only possible if 'arg' is a directory */
|
||||
estr = "is a directory";
|
||||
else
|
||||
estr = strerror(errno);
|
||||
errno = oerrno;
|
||||
#else
|
||||
if (strcmp(arg, "-") == 0 || app_access(arg, W_OK) == 0
|
||||
|| errno == ENOENT)
|
||||
break;
|
||||
estr = strerror(errno);
|
||||
#endif
|
||||
BIO_printf(bio_err,
|
||||
"%s: Cannot open output file %s, %s\n",
|
||||
prog, arg, strerror(errno));
|
||||
prog, arg, estr);
|
||||
return -1;
|
||||
case 'p':
|
||||
case 'n':
|
||||
|
||||
@@ -293,24 +293,6 @@ static int ends_with_dirsep(const char *path)
|
||||
return *path == '/';
|
||||
}
|
||||
|
||||
static int massage_filename(char *name)
|
||||
{
|
||||
# ifdef __VMS
|
||||
char *p = strchr(name, ';');
|
||||
char *q = p;
|
||||
|
||||
if (q != NULL) {
|
||||
for (q++; *q != '\0'; q++) {
|
||||
if (!isdigit((unsigned char)*q))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
# endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a directory; return number of errors found.
|
||||
*/
|
||||
@@ -346,7 +328,6 @@ static int do_dir(const char *dirname, enum Hash h)
|
||||
}
|
||||
while ((filename = OPENSSL_DIR_read(&d, dirname)) != NULL) {
|
||||
if ((copy = strdup(filename)) == NULL
|
||||
|| !massage_filename(copy)
|
||||
|| sk_OPENSSL_STRING_push(files, copy) == 0) {
|
||||
BIO_puts(bio_err, "out of memory\n");
|
||||
exit(1);
|
||||
|
||||
@@ -58,6 +58,11 @@ int generate_cookie_callback(SSL *ssl, unsigned char *cookie,
|
||||
int verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
unsigned int cookie_len);
|
||||
|
||||
int generate_stateless_cookie_callback(SSL *ssl, unsigned char *cookie,
|
||||
size_t *cookie_len);
|
||||
int verify_stateless_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
size_t cookie_len);
|
||||
|
||||
typedef struct ssl_excert_st SSL_EXCERT;
|
||||
|
||||
void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc);
|
||||
|
||||
+21
-2
@@ -231,6 +231,9 @@ static const char *get_sigtype(int nid)
|
||||
case NID_ED25519:
|
||||
return "Ed25519";
|
||||
|
||||
case NID_ED448:
|
||||
return "Ed448";
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -533,9 +536,9 @@ static STRINT_PAIR handshakes[] = {
|
||||
{", CertificateVerify", SSL3_MT_CERTIFICATE_VERIFY},
|
||||
{", ClientKeyExchange", SSL3_MT_CLIENT_KEY_EXCHANGE},
|
||||
{", Finished", SSL3_MT_FINISHED},
|
||||
{", CertificateUrl", 21},
|
||||
{", CertificateUrl", SSL3_MT_CERTIFICATE_URL},
|
||||
{", CertificateStatus", SSL3_MT_CERTIFICATE_STATUS},
|
||||
{", SupplementalData", 23},
|
||||
{", SupplementalData", SSL3_MT_SUPPLEMENTAL_DATA},
|
||||
{", KeyUpdate", SSL3_MT_KEY_UPDATE},
|
||||
#ifndef OPENSSL_NO_NEXTPROTONEG
|
||||
{", NextProto", SSL3_MT_NEXT_PROTO},
|
||||
@@ -752,6 +755,22 @@ int verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int generate_stateless_cookie_callback(SSL *ssl, unsigned char *cookie,
|
||||
size_t *cookie_len)
|
||||
{
|
||||
unsigned int temp;
|
||||
int res = generate_cookie_callback(ssl, cookie, &temp);
|
||||
*cookie_len = temp;
|
||||
return res;
|
||||
}
|
||||
|
||||
int verify_stateless_cookie_callback(SSL *ssl, const unsigned char *cookie,
|
||||
size_t cookie_len)
|
||||
{
|
||||
return verify_cookie_callback(ssl, cookie, cookie_len);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
+5
-11
@@ -197,19 +197,13 @@ static int psk_use_session_cb(SSL *s, const EVP_MD *md,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key_len == EVP_MD_size(EVP_sha256()))
|
||||
cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id);
|
||||
else if (key_len == EVP_MD_size(EVP_sha384()))
|
||||
cipher = SSL_CIPHER_find(s, tls13_aes256gcmsha384_id);
|
||||
|
||||
/* We default to SHA-256 */
|
||||
cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id);
|
||||
if (cipher == NULL) {
|
||||
/* Doesn't look like a suitable TLSv1.3 key. Ignore it */
|
||||
OPENSSL_free(key);
|
||||
*id = NULL;
|
||||
*idlen = 0;
|
||||
*sess = NULL;
|
||||
return 1;
|
||||
BIO_printf(bio_err, "Error finding suitable ciphersuite\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
usesess = SSL_SESSION_new();
|
||||
if (usesess == NULL
|
||||
|| !SSL_SESSION_set1_master_key(usesess, key, key_len)
|
||||
|
||||
+9
-7
@@ -208,14 +208,10 @@ static int psk_find_session_cb(SSL *ssl, const unsigned char *identity,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key_len == EVP_MD_size(EVP_sha256()))
|
||||
cipher = SSL_CIPHER_find(ssl, tls13_aes128gcmsha256_id);
|
||||
else if (key_len == EVP_MD_size(EVP_sha384()))
|
||||
cipher = SSL_CIPHER_find(ssl, tls13_aes256gcmsha384_id);
|
||||
|
||||
/* We default to SHA256 */
|
||||
cipher = SSL_CIPHER_find(ssl, tls13_aes128gcmsha256_id);
|
||||
if (cipher == NULL) {
|
||||
/* Doesn't look like a suitable TLSv1.3 key. Ignore it */
|
||||
OPENSSL_free(key);
|
||||
BIO_printf(bio_err, "Error finding suitable ciphersuite\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1570,6 +1566,8 @@ int s_server_main(int argc, char *argv[])
|
||||
break;
|
||||
case OPT_EARLY_DATA:
|
||||
early_data = 1;
|
||||
if (max_early_data == -1)
|
||||
max_early_data = SSL3_RT_MAX_PLAIN_LENGTH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2040,6 +2038,10 @@ int s_server_main(int argc, char *argv[])
|
||||
SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie_callback);
|
||||
SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie_callback);
|
||||
|
||||
/* Set TLS1.3 cookie generation and verification callbacks */
|
||||
SSL_CTX_set_stateless_cookie_generate_cb(ctx, generate_stateless_cookie_callback);
|
||||
SSL_CTX_set_stateless_cookie_verify_cb(ctx, verify_stateless_cookie_callback);
|
||||
|
||||
if (ctx2 != NULL) {
|
||||
SSL_CTX_set_verify(ctx2, s_server_verify, verify_callback);
|
||||
if (!SSL_CTX_set_session_id_context(ctx2,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ const OPTIONS sess_id_options[] = {
|
||||
{"outform", OPT_OUTFORM, 'f',
|
||||
"Output format - default PEM (PEM, DER or NSS)"},
|
||||
{"in", OPT_IN, 's', "Input file - default stdin"},
|
||||
{"out", OPT_OUT, 's', "Output file - default stdout"},
|
||||
{"out", OPT_OUT, '>', "Output file - default stdout"},
|
||||
{"text", OPT_TEXT, '-', "Print ssl session id details"},
|
||||
{"cert", OPT_CERT, '-', "Output certificate "},
|
||||
{"noout", OPT_NOOUT, '-', "Don't output the encoded session info"},
|
||||
|
||||
+6
-4
@@ -118,7 +118,7 @@
|
||||
#define RSA_NUM 7
|
||||
#define DSA_NUM 3
|
||||
|
||||
#define EC_NUM 17
|
||||
#define EC_NUM 18
|
||||
#define MAX_ECDH_SIZE 256
|
||||
#define MISALIGN 64
|
||||
|
||||
@@ -533,6 +533,7 @@ static OPT_PAIR rsa_choices[] = {
|
||||
#define R_EC_B409 14
|
||||
#define R_EC_B571 15
|
||||
#define R_EC_X25519 16
|
||||
#define R_EC_X448 17
|
||||
#ifndef OPENSSL_NO_EC
|
||||
static OPT_PAIR ecdsa_choices[] = {
|
||||
{"ecdsap160", R_EC_P160},
|
||||
@@ -572,6 +573,7 @@ static OPT_PAIR ecdh_choices[] = {
|
||||
{"ecdhb409", R_EC_B409},
|
||||
{"ecdhb571", R_EC_B571},
|
||||
{"ecdhx25519", R_EC_X25519},
|
||||
{"ecdhx448", R_EC_X448},
|
||||
{NULL}
|
||||
};
|
||||
#endif
|
||||
@@ -1377,7 +1379,7 @@ int speed_main(int argc, char **argv)
|
||||
NID_sect233r1, NID_sect283r1, NID_sect409r1,
|
||||
NID_sect571r1,
|
||||
/* Other */
|
||||
NID_X25519
|
||||
NID_X25519, NID_X448
|
||||
};
|
||||
static const char *test_curves_names[EC_NUM] = {
|
||||
/* Prime Curves */
|
||||
@@ -1389,7 +1391,7 @@ int speed_main(int argc, char **argv)
|
||||
"nistb233", "nistb283", "nistb409",
|
||||
"nistb571",
|
||||
/* Other */
|
||||
"X25519"
|
||||
"X25519", "X448"
|
||||
};
|
||||
static const int test_curves_bits[EC_NUM] = {
|
||||
160, 192, 224,
|
||||
@@ -1397,7 +1399,7 @@ int speed_main(int argc, char **argv)
|
||||
163, 233, 283,
|
||||
409, 571, 163,
|
||||
233, 283, 409,
|
||||
571, 253 /* X25519 */
|
||||
571, 253, 448
|
||||
};
|
||||
|
||||
int ecdsa_doit[EC_NUM] = { 0 };
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
/*
|
||||
* Copyright 2004-2018 The OpenSSL Project Authors. All Rights Reserved.
|
||||
* Copyright (c) 2004, EdelKey Project. All Rights Reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Originally written by Christophe Renou and Peter Sylvester,
|
||||
* for the EdelKey project.
|
||||
*/
|
||||
|
||||
#include <openssl/opensslconf.h>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/store.h>
|
||||
#include <openssl/x509v3.h> /* s2i_ASN1_INTEGER */
|
||||
|
||||
static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
|
||||
int expected, int criterion, OSSL_STORE_SEARCH *search,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!{- $config{hashbangperl} -}
|
||||
#!{- $config{HASHBANGPERL} -}
|
||||
# Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
|
||||
# Copyright (c) 2002 The OpenTSA Project. All rights reserved.
|
||||
#
|
||||
|
||||
+2
-1
@@ -105,7 +105,8 @@ opthelp:
|
||||
dirty = version = 1;
|
||||
break;
|
||||
case OPT_A:
|
||||
seed = cflags = version = date = platform = dir = engdir = 1;
|
||||
seed = options = cflags = version = date = platform = dir = engdir
|
||||
= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user