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 = 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 or greater. + =item B = 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