[stunnel-users] Patch: verify_depth and remote_subj
Michael Smith
msmith at cbnco.com
Thu May 26 17:52:44 CEST 2005
Hi,
I've attached a patch against stunnel 4.10. It adds two verification
options to the global configuration:
===
remote_subj = expected subject for the remote certificate
If set, the remote certificate's subject must match this string. You can
obtain the subject in the proper format by running
openssl x509 -in servercert.pem -noout -subject
Example:
remote_subj = /C=CA/ST=Ontario/CN=gw
Requires verify=2 or greater.
verify_depth = maximum number of CA certificates in chain
Specifies the maximum certificate chain depth when verifying the peer
certificate:
0: peer certificate must be self-signed
1: peer certificate must be signed by a root CA
2: peer certificate can be signed by at most one intermediate CA
The stunnel default depth is 9 for backwards compatibility. Please note
that Apache/SSL defaults to 1. If your non-CA certificates are marked with
the X509v3 basic constraint "CA:FALSE", you probably do not need to worry
about chain depth. This is the default behaviour for most CA tools,
including the scripts that ship with OpenSSL.
===
remote_subj probably belongs in per-service config, not global, but the
SSL verification setup is global and I didn't want to start messing with
it.
Mike
-------------- next part --------------
Index: src/options.c
===================================================================
RCS file: /sourceforge/cvs/abrazo/base/os/packages/stunnel/src/options.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -c -r1.1.1.1 -r1.3
*** src/options.c 20 May 2005 17:00:09 -0000 1.1.1.1
--- src/options.c 26 May 2005 15:25:54 -0000 1.3
***************
*** 664,669 ****
--- 664,710 ----
break;
}
+ /* remote_subj */
+ switch(cmd) {
+ case CMD_INIT:
+ options.remote_subj=NULL;
+ break;
+ case CMD_EXEC:
+ if(strcasecmp(opt, "remote_subj"))
+ break;
+ options.remote_subj=stralloc(arg);
+ return NULL; /* OK */
+ case CMD_DEFAULT:
+ break;
+ case CMD_HELP:
+ log_raw("%-15s = expected subject (DN) for the remote certificate",
+ "remote_subj");
+ break;
+ }
+
+ /* verify_depth */
+ switch(cmd) {
+ case CMD_INIT:
+ /* The OpenSSL default depth is 9. Apache/SSL defaults to 1. */
+ options.verify_depth=9;
+ break;
+ case CMD_EXEC:
+ if(strcasecmp(opt, "verify_depth"))
+ break;
+ options.verify_depth=atoi(arg);
+ if (options.verify_depth < 0)
+ return "verify_depth must be >= 0";
+ return NULL; /* OK */
+ case CMD_DEFAULT:
+ log_raw("%-15s = 9", "verify_depth");
+ break;
+ case CMD_HELP:
+ log_raw("%-15s = maximum certificate chain length (0 == peer only, "
+ "1 == peer and CA, 9 == peer, 8 intermediaries and 1 CA)",
+ "verify_depth");
+ break;
+ }
+
if(cmd==CMD_EXEC)
return option_not_found;
return NULL; /* OK */
Index: src/prototypes.h
===================================================================
RCS file: /sourceforge/cvs/abrazo/base/os/packages/stunnel/src/prototypes.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -c -r1.1.1.1 -r1.3
*** src/prototypes.h 20 May 2005 17:00:09 -0000 1.1.1.1
--- src/prototypes.h 26 May 2005 15:25:54 -0000 1.3
***************
*** 138,143 ****
--- 138,146 ----
#endif
char *output_file;
+ char *remote_subj; /* remote cert's DN must match this */
+ int verify_depth; /* max verify chain depth */
+
/* on/off switches */
struct {
unsigned int cert:1;
Index: src/ssl.c
===================================================================
RCS file: /sourceforge/cvs/abrazo/base/os/packages/stunnel/src/ssl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -c -r1.1.1.1 -r1.3
*** src/ssl.c 20 May 2005 17:00:09 -0000 1.1.1.1
--- src/ssl.c 26 May 2005 15:25:54 -0000 1.3
***************
*** 455,460 ****
--- 455,467 ----
static void verify_init(void) {
X509_LOOKUP *lookup;
+ if (options.remote_subj &&
+ (options.verify_level < 0
+ || (options.verify_level & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0)) {
+ s_log(LOG_ERR, "remote_subj requires at least verify=2");
+ exit(1);
+ }
+
if(options.verify_level<0)
return; /* No certificate verification */
***************
*** 532,554 ****
SSL_CTX_set_verify(ctx, options.verify_level==SSL_VERIFY_NONE ?
SSL_VERIFY_PEER : options.verify_level, verify_callback);
if(options.ca_dir && options.verify_use_only_my)
s_log(LOG_NOTICE, "Peer certificate location %s", options.ca_dir);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *callback_ctx) {
/* our verify callback function */
! char txt[STRLEN];
X509_OBJECT ret;
X509_NAME_oneline(X509_get_subject_name(callback_ctx->current_cert),
! txt, STRLEN);
safestring(txt);
if(options.verify_level==SSL_VERIFY_NONE) {
s_log(LOG_NOTICE, "VERIFY IGNORE: depth=%d, %s",
callback_ctx->error_depth, txt);
return 1; /* Accept connection */
}
if(!preverify_ok) {
/* Remote site specified a certificate, but it's not correct */
s_log(LOG_WARNING, "VERIFY ERROR: depth=%d, error=%s: %s",
--- 539,578 ----
SSL_CTX_set_verify(ctx, options.verify_level==SSL_VERIFY_NONE ?
SSL_VERIFY_PEER : options.verify_level, verify_callback);
+ /*
+ * Bump up the verify depth by 1; we'll catch it in verify_callback
+ * and report that the chain is too long. Otherwise OpenSSL reports
+ * that the chain is incomplete, rather overlong.
+ */
+ SSL_CTX_set_verify_depth(ctx, options.verify_depth + 1);
+
if(options.ca_dir && options.verify_use_only_my)
s_log(LOG_NOTICE, "Peer certificate location %s", options.ca_dir);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *callback_ctx) {
/* our verify callback function */
! char txt[STRLEN], raw[STRLEN];
X509_OBJECT ret;
X509_NAME_oneline(X509_get_subject_name(callback_ctx->current_cert),
! raw, STRLEN);
!
! strncpy(txt, raw, STRLEN);
! txt[STRLEN-1] = '\0';
safestring(txt);
+
if(options.verify_level==SSL_VERIFY_NONE) {
s_log(LOG_NOTICE, "VERIFY IGNORE: depth=%d, %s",
callback_ctx->error_depth, txt);
return 1; /* Accept connection */
}
+ if (callback_ctx->error_depth > options.verify_depth) {
+ s_log(LOG_WARNING, "VERIFY ERROR: certificate chain too long at %s",
+ txt);
+ X509_STORE_CTX_set_error(callback_ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
+ return 0; /* Reject connection */
+ }
if(!preverify_ok) {
/* Remote site specified a certificate, but it's not correct */
s_log(LOG_WARNING, "VERIFY ERROR: depth=%d, error=%s: %s",
***************
*** 564,570 ****
}
if(revocation_store && !crl_callback(callback_ctx))
return 0; /* Reject connection */
! /* errnum = X509_STORE_CTX_get_error(ctx); */
s_log(LOG_NOTICE, "VERIFY OK: depth=%d, %s", callback_ctx->error_depth, txt);
return 1; /* Accept connection */
--- 588,609 ----
}
if(revocation_store && !crl_callback(callback_ctx))
return 0; /* Reject connection */
!
! /*
! * Check the subject name on the peer certificate (depth 0).
! * You should set verifydepth=1 so that only the peer and root CA are
! * considered. Otherwise someone with a valid cert issued by your CA
! * could potentially trick you by issuing another cert with the same
! * name as remote_subj.
! */
! if(options.remote_subj && callback_ctx->error_depth == 0
! && strcmp(raw, options.remote_subj) != 0)
! {
! s_log(LOG_WARNING, "VERIFY ERROR: remote subject %s does not "
! "match configured remote_subj %s",
! txt, options.remote_subj);
! return 0; /* Reject connection */
! }
s_log(LOG_NOTICE, "VERIFY OK: depth=%d, %s", callback_ctx->error_depth, txt);
return 1; /* Accept connection */
Index: doc/stunnel.pod
===================================================================
RCS file: /sourceforge/cvs/abrazo/base/os/packages/stunnel/doc/stunnel.pod,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -c -r1.1.1.1 -r1.2
*** doc/stunnel.pod 20 May 2005 17:00:09 -0000 1.1.1.1
--- doc/stunnel.pod 26 May 2005 15:38:01 -0000 1.2
***************
*** 333,340 ****
level 3 - verify peer with locally installed certificate
default - no verify
! =back
=head2 SERVICE-LEVEL OPTIONS
--- 333,367 ----
level 3 - verify peer with locally installed certificate
default - no verify
! =item B<remote_subj> = expected subject for the remote certificate
!
! If set, the remote certificate's subject must match this string. You can
! obtain the subject in the proper format by running
!
! openssl x509 -in servercert.pem -noout -subject
!
! Example:
!
! remote_subj = /C=CA/ST=Ontario/CN=gw
!
! Requires B<verify=2> or greater.
+ =item B<verify_depth> = maximum number of CA certificates in chain
+
+ Specifies the maximum certificate chain depth when verifying the peer
+ certificate:
+
+ 0: peer certificate must be self-signed
+ 1: peer certificate must be signed by a root CA
+ 2: peer certificate can be signed by at most one intermediate CA
+
+ The stunnel default depth is 9 for backwards compatibility. Please note that
+ Apache/SSL defaults to 1. If your non-CA certificates are marked with the
+ X509v3 basic constraint "CA:FALSE", you probably do not need to worry about
+ chain depth. This is the default behaviour for most CA tools, including
+ the scripts that ship with OpenSSL.
+
+ =back
=head2 SERVICE-LEVEL OPTIONS
More information about the stunnel-users
mailing list