Pulling in changes from James' 2.1/openvpn branch in SVN.
Conflicts:
buffer.c
init.c
manage.h
multi.c
openvpn.8
options.c
ssl.c
version.m4
win/sign.py
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
/*
* This is necessary due to certain buggy implementations of snprintf,
* that don't guarantee null termination for size > 0.
+ *
+ * Return false on overflow.
++ *
+ * This function is duplicated into service-win32/openvpnserv.c
+ * Any modifications here should be done to the other place as well.
*/
- int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+ bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
{
va_list arglist;
- int ret = 0;
+ int len = -1;
if (size > 0)
{
va_start (arglist, format);
}
if (flags & M_FATAL)
- msg (M_INFO, "Exiting");
+ msg (M_INFO, "Exiting due to fatal error");
- mutex_unlock_static (L_MSG);
-
if (flags & M_FATAL)
openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
const struct plugin_list *plugins,
struct env_set *es)
{
- if (!options->route_noexec && route_list)
+ if (!options->route_noexec && ( route_list || route_ipv6_list ) )
- add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+ {
- add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
++ add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+ setenv_int (es, "redirect_gateway", route_list->did_redirect_default_gateway);
+ }
+ #ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_up_down (management, "UP", es);
+ #endif
if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
{
#endif
to.verify_command = options->tls_verify;
+ to.verify_export_cert = options->tls_export_cert;
to.verify_x509name = options->tls_remote;
to.crl_file = options->crl_file;
+ to.ssl_flags = options->ssl_flags;
to.ns_cert_type = options->ns_cert_type;
memmove (to.remote_cert_ku, options->remote_cert_ku, sizeof (to.remote_cert_ku));
to.remote_cert_eku = options->remote_cert_eku;
mi->context.c2.push_ifconfig_defined = true;
mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
+ #ifdef ENABLE_CLIENT_NAT
+ mi->context.c2.push_ifconfig_local_alias = mi->context.options.push_ifconfig_local_alias;
+ #endif
+
+ /* the current implementation does not allow "static IPv4, pool IPv6",
+ * (see below) so issue a warning if that happens - don't break the
+ * session, though, as we don't even know if this client WANTS IPv6
+ */
+ if ( mi->context.c1.tuntap->ipv6 &&
+ mi->context.options.ifconfig_ipv6_pool_defined &&
+ ! mi->context.options.push_ifconfig_ipv6_defined )
+ {
+ msg( M_INFO, "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, automatic IPv6 assignment from --ifconfig-ipv6-pool does not work. Use --ifconfig-ipv6-push for IPv6 then." );
+ }
}
else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
{
to set the TCP/IP properties of the client's TUN/TAP interface.
.\"*********************************************************
.TP
-.B --allow-pull-fqdn
+.B \-\-allow-pull-fqdn
Allow client to pull DNS names from server (rather than being limited
to IP address) for
-.B --ifconfig,
-.B --route,
+.B \-\-ifconfig,
+.B \-\-route,
and
-.B --route-gateway.
+.B \-\-route-gateway.
.\"*********************************************************
.TP
-.B --client-nat snat|dnat network netmask alias
++.B \-\-client-nat snat|dnat network netmask alias
+ This pushable client option sets up a stateless one-to-one NAT
+ rule on packet addresses (not ports), and is useful in cases
+ where routes or ifconfig settings pushed to the client would
+ create an IP numbering conflict.
+
+ .B network/netmask
+ (for example 192.168.0.0/255.255.0.0)
+ defines the local view of a resource from the client perspective, while
+ .B alias/netmask
+ (for example 10.64.0.0/255.255.0.0)
+ defines the remote view from the server perspective.
+
+ Use
+ .B snat
+ (source NAT) for resources owned by the client and
+ .B dnat
+ (destination NAT) for remote resources.
+
+ Set
-.B --verb 6
++.B \-\-verb 6
+ for debugging info showing the transformation of src/dest
+ addresses in packets.
+ .\"*********************************************************
+ .TP
-.B --redirect-gateway flags...
+.B \-\-redirect-gateway flags...
(Experimental) Automatically execute routing commands to cause all outgoing IP traffic
to be redirected over the VPN.
by the management channel.
.\"*********************************************************
.TP
-.B --management-up-down
++.B \-\-management-up-down
+ Report tunnel up/down events to management interface.
+ .B
+ .\"*********************************************************
+ .TP
-.B --management-client-auth
+.B \-\-management-client-auth
Gives management interface client the responsibility
to authenticate clients after their client certificate
has been verified. See management-notes.txt in OpenVPN
which is functionally equivalent.
.\"*********************************************************
.TP
- .B \-\-ifconfig-push local remote-netmask
-.B --ifconfig-push local remote-netmask [alias]
++.B \-\-ifconfig-push local remote-netmask [alias]
Push virtual IP endpoints for client tunnel,
-overriding the --ifconfig-pool dynamic allocation.
+overriding the \-\-ifconfig-pool dynamic allocation.
The parameters
.B local
know what you are doing!
.\"*********************************************************
.TP
- .B \-\-port-share host port
-.B --port-share host port [dir]
++.B \-\-port-share host port [dir]
When run in TCP server mode, share the OpenVPN port with
another application, such as an HTTPS server. If OpenVPN
senses a connection to its port which is using a non-OpenVPN
).
.\"*********************************************************
.TP
-.B --extra-certs file
++.B \-\-extra-certs file
+ Specify a
+ .B file
+ containing one or more PEM certs (concatenated together)
+ that complete the
+ local certificate chain.
+
+ This option is useful for "split" CAs, where the CA for server
+ certs is different than the CA for client certs. Putting certs
+ in this file allows them to be used to complete the local
+ certificate chain without trusting them to verify the peer-submitted
+ certificate, as would be the case if the certs were placed in the
+ .B ca
+ file.
+ .\"*********************************************************
+ .TP
-.B --key file
+.B \-\-key file
Local peer's private key in .pem format. Use the private key which was generated
when you built your peer's certificate (see
.B -cert file
Specify a PKCS #12 file containing local private key,
local certificate, and root CA certificate.
This option can be used instead of
-.B --ca, --cert,
+.B \-\-ca, \-\-cert,
and
-.B --key.
+.B \-\-key.
.\"*********************************************************
.TP
-.B --verify-hash hash
++.B \-\-verify-hash hash
+ Specify SHA1 fingerprint for level-1 cert. The level-1 cert is the
+ CA (or intermediate cert) that signs the leaf certificate, and is
+ one removed from the leaf certificate in the direction of the root.
+ When accepting a connection from a peer, the level-1 cert
+ fingerprint must match
+ .B hash
+ or certificate verification will fail. Hash is specified
+ as XX:XX:... For example: AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+ .\"*********************************************************
+ .TP
-.B --pkcs11-cert-private [0|1]...
+.B \-\-pkcs11-cert-private [0|1]...
Set if access to certificate object should be performed after login.
Every provider has its own setting.
.\"*********************************************************
See the "Environmental Variables" section below for
additional parameters passed as environmental variables.
-
-Note that
-.B cmd
-can be a shell command with multiple arguments, in which
-case all OpenVPN-generated arguments will be appended
-to
-.B cmd
-to build a command line which will be passed to the script.
.\"*********************************************************
.TP
-.B --tls-remote name
+.B \-\-tls-export-cert directory
+Store the certificates the clients uses upon connection to this
- directory. This will be done before --tls-verify is called. The
++directory. This will be done before \-\-tls-verify is called. The
+certificates will use a temporary name and will be deleted when
+the tls-verify script returns. The file name used for the certificate
+is available via the peer_cert environment variable.
+.\"*********************************************************
+.TP
+.B \-\-tls-remote name
Accept connections only from a host with X509 name
or common name equal to
.B name.
environment too.
.\"*********************************************************
.TP
-.B --x509-track attribute
++.B \-\-x509-track attribute
+ Save peer X509
+ .B attribute
+ value in environment for use by plugins and management interface.
+ Prepend a '+' to
+ .B attribute
+ to save values from full cert chain. Values will be encoded
+ as X509_<depth>_<attribute>=<value>. Multiple
-.B --x509-track
++.B \-\-x509-track
+ options can be defined to track multiple attributes.
+ .\"*********************************************************
+ .TP
-.B --ns-cert-type client|server
+.B \-\-ns-cert-type client|server
Require that peer certificate was signed with an explicit
.B nsCertType
designation of "client" or "server".
attempts to connect to another client by impersonating the server.
The attack is easily prevented by having clients verify
the server certificate using any one of
-.B --remote-cert-tls, --tls-remote,
+.B \-\-remote-cert-tls, \-\-tls-remote,
or
-.B --tls-verify.
+.B \-\-tls-verify.
.\"*********************************************************
.TP
- .B \-\-crl-verify crl
-.B --crl-verify crl ['dir']
++.B \-\-crl-verify crl ['dir']
Check peer certificate against the file
.B crl
in PEM format.
/* --ifconfig endpoints to be pushed to client */
bool push_reply_deferred;
bool push_ifconfig_defined;
+ bool sent_push_reply;
in_addr_t push_ifconfig_local;
in_addr_t push_ifconfig_remote_netmask;
+ #ifdef ENABLE_CLIENT_NAT
+ in_addr_t push_ifconfig_local_alias;
+ #endif
+ bool push_ifconfig_ipv6_defined;
+ struct in6_addr push_ifconfig_ipv6_local;
+ int push_ifconfig_ipv6_netbits;
+ struct in6_addr push_ifconfig_ipv6_remote;
+
/* client authentication state, CAS_SUCCEEDED must be 0 */
# define CAS_SUCCEEDED 0
# define CAS_PENDING 1
"--key file : Local private key in .pem format.\n"
"--pkcs12 file : PKCS#12 file containing local private key, local certificate\n"
" and optionally the root CA certificate.\n"
+#ifdef ENABLE_X509ALTUSERNAME
+ "--x509-username-field : Field used in x509 certificat to be username.\n"
+ " Default is CN.\n"
+#endif
+ "--verify-hash : Specify SHA1 fingerprint for level-1 cert.\n"
#ifdef WIN32
"--cryptoapicert select-string : Load the certificate and private key from the\n"
" Windows Certificate System Store.\n"
options->routes = new_route_option_list (options->max_routes, &options->gc);
}
+void
+rol6_check_alloc (struct options *options)
+{
+ if (!options->routes_ipv6)
+ options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
+}
+
+ #ifdef ENABLE_CLIENT_NAT
+ static void
+ cnol_check_alloc (struct options *options)
+ {
+ if (!options->client_nat)
+ options->client_nat = new_client_nat_list (&options->gc);
+ }
+ #endif
+
#ifdef ENABLE_DEBUG
static void
show_connection_entry (const struct connection_entry *o)
struct push_entry *e = c->options.push_list.head;
bool multi_push = false;
static char cmd[] = "PUSH_REPLY";
- const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
+ const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
const int safe_cap = BCAP (&buf) - extra;
+ bool push_sent = false;
- buf_printf (&buf, cmd);
+ msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
+
+ buf_printf (&buf, "%s", cmd);
+
+ if ( c->c2.push_ifconfig_ipv6_defined )
+ {
+ /* IPv6 is put into buffer first, could be lengthy */
+ /* TODO: push "/netbits" as well, to allow non-/64 subnet sizes
+ * (needs changes in options.c, options.h, and other places)
+ */
+ buf_printf( &buf, ",ifconfig-ipv6 %s %s",
+ print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
+ print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
+ if (BLEN (&buf) >= safe_cap)
+ {
+ msg (M_WARN, "--push ifconfig-ipv6 option is too long");
+ goto fail;
+ }
+ }
while (e)
{
} \
}
- int ret = 0;
+/*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+ * (copied from ../buffer.c, line 217)
+ * (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c)
+ */
+
+int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list arglist;
- ret = vsnprintf (str, size, format, arglist);
++ int len = -1;
+ if (size > 0)
+ {
+ va_start (arglist, format);
- return ret;
++ len = vsnprintf (str, size, format, arglist);
+ va_end (arglist);
+ str[size - 1] = 0;
+ }
++ return (len >= 0 && len < size);
+}
+
+
bool
init_security_attributes_allow_all (struct security_attributes *obj)
{
}
}
+#ifdef ENABLE_X509ALTUSERNAME
+static
+bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size)
+{
+ bool retval = false;
+ X509_EXTENSION *pExt;
+ char *buf = 0;
+ int length = 0;
+ GENERAL_NAMES *extensions;
+ int nid = OBJ_txt2nid(fieldname);
+
+ extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL);
+ if ( extensions )
+ {
+ int numalts;
+ int i;
+ /* get amount of alternatives,
+ * RFC2459 claims there MUST be at least
+ * one, but we don't depend on it...
+ */
+
+ numalts = sk_GENERAL_NAME_num(extensions);
+
+ /* loop through all alternatives */
+ for (i=0; i<numalts; i++)
+ {
+ /* get a handle to alternative name number i */
+ const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i );
+
+ switch (name->type)
+ {
+ case GEN_EMAIL:
+ ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5);
+ if ( strlen (buf) != name->d.ia5->length )
+ {
+ msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero");
+ OPENSSL_free (buf);
+ } else {
+ strncpynt(out, buf, size);
+ OPENSSL_free(buf);
+ retval = true;
+ }
+ break;
+ default:
+ msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i",
+ name->type);
+ break;
+ }
+ }
+ sk_GENERAL_NAME_free (extensions);
+ }
+ return retval;
+ #ifdef ENABLE_X509_TRACK
+ /*
+ * setenv_x509_track function -- save X509 fields to environment,
+ * using the naming convention:
+ *
+ * X509_{cert_depth}_{name}={value}
+ *
+ * This function differs from setenv_x509 below in the following ways:
+ *
+ * (1) Only explicitly named attributes in xt are saved, per usage
+ * of --x509-track program options.
+ * (2) Only the level 0 cert info is saved unless the XT_FULL_CHAIN
+ * flag is set in xt->flags (corresponds with prepending a '+'
+ * to the name when specified by --x509-track program option).
+ * (3) This function supports both X509 subject name fields as
+ * well as X509 V3 extensions.
+ */
+
+ /* worker method for setenv_x509_track */
+ static void
+ do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth)
+ {
+ char *name_expand;
+ size_t name_expand_size;
+
+ string_mod (value, CC_ANY, CC_CRLF, '?');
+ msg (D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
+ name_expand_size = 64 + strlen (name);
+ name_expand = (char *) malloc (name_expand_size);
+ check_malloc_return (name_expand);
+ openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", depth, name);
+ setenv_str (es, name_expand, value);
+ free (name_expand);
+ }
+
+ static void
+ setenv_x509_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509)
+ {
+ X509_NAME *x509_name = X509_get_subject_name (x509);
+ const char nullc = '\0';
+ int i;
+
+ while (xt)
+ {
+ if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
+ {
+ i = X509_NAME_get_index_by_NID(x509_name, xt->nid, -1);
+ if (i >= 0)
+ {
+ X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
+ if (ent)
+ {
+ ASN1_STRING *val = X509_NAME_ENTRY_get_data (ent);
+ unsigned char *buf;
+ buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
+ if (ASN1_STRING_to_UTF8 (&buf, val) > 0)
+ {
+ do_setenv_x509(es, xt->name, (char *)buf, depth);
+ OPENSSL_free (buf);
+ }
+ }
+ }
+ else
+ {
+ i = X509_get_ext_by_NID(x509, xt->nid, -1);
+ if (i >= 0)
+ {
+ X509_EXTENSION *ext = X509_get_ext(x509, i);
+ if (ext)
+ {
+ BIO *bio = BIO_new(BIO_s_mem());
+ if (bio)
+ {
+ if (X509V3_EXT_print(bio, ext, 0, 0))
+ {
+ if (BIO_write(bio, &nullc, 1) == 1)
+ {
+ char *str;
+ BIO_get_mem_data(bio, &str);
+ do_setenv_x509(es, xt->name, str, depth);
+ }
+ }
+ BIO_free(bio);
+ }
+ }
+ }
+ }
+ }
+ xt = xt->next;
+ }
}
#endif
/* check peer cert against CRL */
if (opt->crl_file)
{
- X509_CRL *crl=NULL;
- X509_REVOKED *revoked;
- BIO *in=NULL;
- int n,i,retval = 0;
+ if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
+ {
+ char fn[256];
+ int fd;
+ if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", opt->crl_file, OS_SPECIFIC_DIRSEP, serial))
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: filename overflow");
+ goto err;
+ }
+ fd = open (fn, O_RDONLY);
+ if (fd >= 0)
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial);
+ close(fd);
+ goto err;
+ }
+ }
+ else
+ {
+ X509_CRL *crl=NULL;
+ X509_REVOKED *revoked;
+ BIO *in=NULL;
+ int n,i,retval = 0;
- in=BIO_new(BIO_s_file());
+ in=BIO_new(BIO_s_file());
- if (in == NULL) {
- msg (M_ERR, "CRL: BIO err");
- goto end;
- }
- if (BIO_read_filename(in, opt->crl_file) <= 0) {
- msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
- goto end;
- }
- crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
- if (crl == NULL) {
- msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
- goto end;
- }
+ if (in == NULL) {
+ msg (M_ERR, "CRL: BIO err");
+ goto end;
+ }
+ if (BIO_read_filename(in, opt->crl_file) <= 0) {
+ msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
+ goto end;
+ }
+ crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
+ if (crl == NULL) {
+ msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
+ goto end;
+ }
- if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
- msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
- retval = 1;
- goto end;
- }
+ if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
+ msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
+ retval = 1;
+ goto end;
+ }
- n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
- n = sk_num(X509_CRL_get_REVOKED(crl));
++ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
- for (i = 0; i < n; i++) {
- revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
- if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
- msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
- goto end;
- }
- }
- for (i = 0; i < n; i++) {
- revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i);
- if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
- msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
- goto end;
- }
- }
++ for (i = 0; i < n; i++) {
++ revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
++ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
++ msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
++ goto end;
++ }
++ }
- retval = 1;
- msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
+ retval = 1;
+ msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
- end:
+ end:
- BIO_free(in);
- if (crl)
- X509_CRL_free (crl);
- if (!retval)
- goto err;
+ BIO_free(in);
+ if (crl)
+ X509_CRL_free (crl);
+ if (!retval)
+ goto err;
+ }
}
msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
-import sys\r
-from wb import config, choose_arch, home_fn\r
-\r
-if 'SIGNTOOL' in config:\r
- sys.path.append(home_fn(config['SIGNTOOL']))\r
-\r
-def main(conf, arch, tap_dir):\r
- from signtool import SignTool\r
- st = SignTool(conf, tap_dir)\r
- for x64 in choose_arch(arch):\r
- st.sign_verify(x64=x64)\r
-\r
-# if we are run directly, and not loaded as a module\r
-if __name__ == "__main__":\r
- if len(sys.argv) >= 2:\r
- if len(sys.argv) >= 3:\r
- tap_dir = home_fn(sys.argv[2])\r
- else:\r
- tap_dir = None\r
- main(config, sys.argv[1], tap_dir)\r
- else:\r
- print "usage: sign <x64|x86|all> [tap-dir]"\r
- sys.exit(2)\r
+import sys
+from wb import config, choose_arch, home_fn
+
+if 'SIGNTOOL' in config:
+ sys.path.append(home_fn(config['SIGNTOOL']))
+
- def main(conf, arch):
++def main(conf, arch, tap_dir):
+ from signtool import SignTool
- st = SignTool(conf)
++ st = SignTool(conf, tap_dir)
+ for x64 in choose_arch(arch):
+ st.sign_verify(x64=x64)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ if len(sys.argv) >= 2:
- main(config, sys.argv[1])
++ if len(sys.argv) >= 3:
++ tap_dir = home_fn(sys.argv[2])
++ else:
++ tap_dir = None
++ main(config, sys.argv[1], tap_dir)
+ else:
- print "usage: sign <x64|x86|all>"
++ print "usage: sign <x64|x86|all> [tap-dir]"
+ sys.exit(2)