/* the header is necessary to enable some macros #include */ /*//////////////////////////////////////////////////////////////////////// Copyright (c) 1998-2000 Yutaka Sato and ETL,AIST,MITI Copyright (c) 2001-2006 National Institute of Advanced Industrial Science and Technology (AIST) AIST-Product-ID: 2000-ETL-198715-01, H14PRO-049, H15PRO-165, H18PRO-443 Permission to use, copy, modify, and distribute this material for any purpose and without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. ///////////////////////////////////////////////////////////////////////// Content-Type: program/C; charset=US-ASCII Program: sslway.c (SSL encoder/decoder with SSLeay/openSSL) Author: Yutaka Sato Description: Given environment: file descriptor 0 is a socket connected to a client file descriptor 1 is a socket connected to a server Commandline argument: -cert file -- certificate (possibly with private key) of this SSLway to be shown to a peer -key file -- private key file (if not included in the -cert file) -pass arg -- the source of passphrase, pass:string or file:path -CAfile file -- the name of file contains a CA's certificate -CApath dir -- directory contains CA's certificate files each named with `X509 -hash -noout < certificate.pem` -Vrfy -- peer's certificate must be shown and must be authorized -vrfy -- peer's certificate, if shown, must be authorized -Auth -- peer must show its certificate, but it can be unauthorized -auth -- just record the peer's certificate, if exists, into log -- equals to obsoleted "-client_auth". -vd -- detailed logging -vu -- logging with trace (former default) -vt -- terse logging (current default) -vs -- disalbe any logging -ht through pass if the request is in bare HTTP protocol (GET,HEAD,POST) Following options can be omitted when the sslway is called from DeleGate with FCL, FSV or FMD since it will be detected automatically. -co apply SSL for the connection to the server [default for FSV] -ac aplly SSL for the accepted connection from the client [default for FCL] -ad accept either SSL or through by auto-detection of SSL-ClientHello -ss negotiate by AUTH SSL for FTP (implicit SSL for data-connection) -st accept STARTTLS (protocol is auto-detect) and SSL tunneling -St require STARTTLS first (protocol is auto-detect) and PBSZ+PROT for FTP -{ss|st|St}/protocol enable STARTTLS for the protocol {SMTP,POP,IMAP,FTP} -bugs -tls1 just talk TLSv1 -ssl2 just talk SSLv2 -ssl3 just talk SSLv3 Usage: delegated FSV=sslway delegated FCL=sslway ... How to make: - do make at .. or ../src directory - edit Makefile.go to let SSLEAY points to the directory of libssl.a - make -f Makefile.go sslway History: 980412 created 980428 renamed from "sslrelay" to "sslway" //////////////////////////////////////////////////////////////////////#*/ #include #include #include "ystring.h" #include "log.h" #ifdef _MSC_VER #undef ERROR #undef X509 #undef X509_NAME #endif int randstack_call(int strg,iFUNCP func, ...); char **move_envarg(int ac,const char *av[],const char **areap,int *lengp,int *sizep); long Gettimeofday(int *usec); int CFI_init(int ac,const char *av[]); int PollIns(int timeout,int size,int *mask,int *rmask); int setNonblockingIO(int fd,int on); void set_nodelay(int sock,int onoff); int RecvPeek(int sock,void *buf,int len); int SocketOf(int sock); int LIBFILE_IS(PCStr(file),PVStr(xfile)); FILE *CFI_fopenShared(PCStr(mode)); int CFI_sharedLock(); int CFI_exclusiveLock(); int CFI_unLock(); const char *CFI_FILTER_ID(); #define LSILENT -1 #define LERROR 0 #define LTRACE 1 #define LDEBUG 2 static int loglevel = LERROR; #define ERROR (loglevel < LERROR)?0:DOLOG #define TRACE (loglevel < LTRACE)?0:DOLOG #define DEBUG (loglevel < LDEBUG)?0:DOLOG static FILE *stdctl; static const char *client_host; static int PID; static int Builtin; static int DOLOG(PCStr(fmt),...) { CStr(xfmt,256); CStr(head,256); VARGS(8,fmt); if( Builtin ) sprintf(head,"## SSLway"); else sprintf(head,"## SSLway[%d](%s)",PID,client_host); sprintf(xfmt,"%%s %s\n",fmt); syslog_ERROR(xfmt,head,VA8); return 0; } #ifndef SSL_FILETYPE_PEM /*{*/ /*BEGIN_STAB(ssl)*/ #ifdef __cplusplus extern "C" { #endif /* #include "ssl.h" */ #define SSL_FILETYPE_PEM 1 #define SSL_VERIFY_NONE 0x00 #define SSL_VERIFY_PEER 0x01 #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 #define SSL_VERIFY_CLIENT_ONCE 0x04 #define SSL_CTRL_OPTIONS 32 #define SSL_OP_NO_SSLv2 0x01000000 #define SSL_OP_NO_SSLv3 0x02000000 #define SSL_OP_NO_TLSv1 0x04000000 typedef void SSL_CTX; typedef void SSL_METHOD; typedef void SSL; typedef void X509; typedef void X509_NAME; typedef void X509_STORE; typedef void X509_STORE_CTX; typedef void BIO_METHOD; typedef void BIO; typedef void RSA; typedef void SSL_SESSION; typedef void EVP_PKEY; #define BIO_NOCLOSE 0 const char *SSLeay_version(int t); BIO_METHOD *BIO_s_mem(); BIO *BIO_new(BIO_METHOD*); int BIO_puts(BIO *bp,char *buf); int BIO_gets(BIO *bp,char *buf,int size); BIO *BIO_new_fp(FILE *stream, int close_flag); int BIO_free(BIO *a); X509 *PEM_read_bio_X509(BIO*,...); RSA *PEM_read_bio_RSAPrivateKey(BIO*,...); SSL_CTX *SSL_CTX_new(SSL_METHOD *method); int SSL_library_init(void); SSL *SSL_new(SSL_CTX *ctx); int SSL_set_fd(SSL *ssl, int fd); int SSL_connect(SSL *ssl); int SSL_accept(SSL *ssl); int SSL_write(SSL *ssl, const void *buf, int num); int SSL_read(SSL *ssl,void *buf,int num); int SSL_pending(SSL *s); int SSL_shutdown(SSL *ssl); #define SSL_RECEIVED_SHUTDOWN 2 int SSL_get_shutdown(SSL *ssl); void SSL_set_connect_state(SSL *s); void SSL_set_accept_state(SSL *s); void SSL_load_error_strings(void ); int SSL_get_error(SSL *s,int ret_code); X509 *SSL_get_peer_certificate(SSL *ssl); SSL_SESSION *SSL_SESSION_new(void); #define SSL_CTX_sess_set_new_cb(ctx,cb) /* it's a macro */ /* #define SSL_CTRL_GET_SESSION_REUSED 6 #define SSL_CTRL_SESS_HIT 27 #define SSL_CTRL_SESS_MISSES 29 #define SSL_CTX_sess_hits(ctx) \ SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_HIT,0,NULL) #define SSL_CTX_sess_misses(ctx) \ SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_MISSES,0,NULL) #define SSL_session_reused(ssl) \ SSL_ctrl((ssl),SSL_CTRL_GET_SESSION_REUSED,0,NULL) */ #define SSL_session_reused(ssl) 0 int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c); SSL_SESSION *SSL_get_session(SSL *ssl); int SSL_set_session(SSL *ssl,SSL_SESSION *sess); int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); X509 *SSL_get_certificate(SSL *ssl); EVP_PKEY *SSL_get_privatekey(SSL *ssl); #define EVP_PKEY_RSA 6 SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a,unsigned char **pp,long length); int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp); X509 *d2i_X509(X509 **x,unsigned char**in,int len); int i2d_X509(X509 *x,unsigned char **pp); EVP_PKEY *d2i_PrivateKey(int type,EVP_PKEY **a,unsigned char **pp,long length); int i2d_PrivateKey(EVP_PKEY *a,unsigned char **pp); long SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); long SSL_CTX_ctrl(SSL_CTX *ctx,int cmd, long larg, void *parg); int SSL_CTX_check_private_key(SSL_CTX *ctx); X509_STORE *SSL_CTX_get_cert_store(SSL_CTX *); int SSL_CTX_load_verify_locations(SSL_CTX *ctx,PCStr(CAfile),PCStr(CApath)); int SSL_CTX_set_cipher_list(SSL_CTX *,PCStr(str)); typedef int pem_password_cb(char buf[], int size, int rwflag, void *userdata); void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx, RSA *(*cb)(SSL *ssl,int is_export, int keylength)); void SSL_CTX_set_verify(SSL_CTX *ctx,int mode, int (*callback)(int, X509_STORE_CTX *)); int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx,PCStr(file), int type); int SSL_CTX_use_certificate_file(SSL_CTX *ctx,PCStr(file), int type); SSL_METHOD *SSLv2_server_method(); SSL_METHOD *SSLv2_client_method(); SSL_METHOD *SSLv3_server_method(); SSL_METHOD *SSLv3_client_method(); SSL_METHOD *SSLv23_server_method(); SSL_METHOD *SSLv23_client_method(); SSL_METHOD *TLSv1_server_method(); SSL_METHOD *TLSv1_client_method(); X509_NAME *X509_get_issuer_name(X509 *a); int i2d_X509_bio(BIO *bp,X509 *x509); char *X509_NAME_oneline(X509_NAME *a,char buf[],int size); const char *X509_verify_cert_error_string(long n); X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); X509_NAME *X509_get_subject_name(X509 *a); void X509_free(X509 *a); int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); RSA *RSA_generate_key(int bits, unsigned long e,void (*callback)(int,int,void *),void *cb_arg); typedef struct { int ssl_version; unsigned int key_arg_length; unsigned char key_arg[8]; int master_key_length; unsigned char master_key[48]; unsigned int session_id_length; unsigned char session_id[32]; } SessionHead; void ERR_load_crypto_strings(void); RSA *PEM_read_bio_PrivateKey(BIO*,...); X509 *PEM_read_X509(FILE*fp,X509**x,pem_password_cb*cb,void *u); EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp,EVP_PKEY **x,pem_password_cb *cb,void *u); int RSA_size(RSA*rsa); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey); EVP_PKEY *X509_get_pubkey(X509 *x); #define RSA_PKCS1_PADDING 1 #define NID_md5 4 int RSA_private_encrypt(int flen,unsigned char *from,unsigned char *to,RSA *rsa,int padding); int RSA_public_decrypt(int flen,unsigned char *from,unsigned char *to,RSA *rsa,int padding); int RSA_sign(int type,unsigned char *m,unsigned int m_len,unsigned char *sigret,unsigned int *siglen,RSA *rsa); int RSA_verify(int type,unsigned char *m,unsigned int m_len,unsigned char *sigbuf,unsigned int siglen,RSA *rsa); #ifdef __cplusplus } #endif #endif /*}*/ #ifdef __cplusplus extern "C" { #endif unsigned long ERR_get_error(void); char *ERR_error_string_n(int,char*,int); void ERR_print_errors_fp(FILE *fp); void RAND_seed(const void *buf,int num); void X509_STORE_set_flags(X509_STORE *ctx, long flags);/*OPT(0)*/ #define X509_V_FLAG_CRL_CHECK 0x04 #define X509_V_FLAG_CRL_CHECK_ALL 0x08 #define RC4_INT unsigned int typedef struct { RC4_INT x,y; RC4_INT data[256]; } RC4_KEY; void RC4_set_key(RC4_KEY *key,int len,const unsigned char *data); void RC4(RC4_KEY *key,unsigned long len,const unsigned char *in,unsigned char *out); int SSL_CTX_set_session_id_context(SSL_CTX*,const unsigned char *sid_ctx,unsigned int sid_ctx_len); /*OPT(0)*/ typedef int (*GEN_SESSION_CB)(const SSL *ssl,unsigned char *id,unsigned int *id_len); int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb);/*OPT(0)*/ void ENGINE_load_builtin_engines(void);/*OPT(0)*/ void OPENSSL_add_all_algorithms_conf(void);/*OPT(0)*/ #ifdef __cplusplus } #endif /*END_STAB*/ void myRC4_set_key(RC4_KEY *key,int len,const unsigned char *data){ RC4_set_key(key,len,data); } void myRC4(RC4_KEY *key,unsigned long len,const unsigned char *in,unsigned char *out){ RC4(key,len,in,out); } static unsigned char *ssid = (unsigned char*)"SSLway"; static int ssid_len = 1; typedef unsigned char Uchar; int sslway_dl(); static void putDylibError(){ fprintf(stderr,"-- ERROR: can't link the SSL/Crypto library.\n"); fprintf(stderr,"-- Hint: use -vl option to trace the required library,\n"); fprintf(stderr,"--- find it (ex. libssl.so.X.Y.Z) under /usr/lib or /lib,\n"); fprintf(stderr,"--- then set the library version as DYLIB='+,lib*.so.X.Y.Z'\n"); } static int checkCrypt(){ if( sslway_dl() == 0 ){ putDylibError(); return -1; } return 0; } int signRSA(RSA *rsa,PCStr(md5),int mlen,PVStr(sig),unsigned int *slen){ int siz; int ok = 0; if( checkCrypt() < 0 ) return 0; siz = RSA_size(rsa); if( *slen < siz ){ fprintf(stderr,"signRSA: not enough sig. buffer\n"); return 0; } ok = RSA_sign(NID_md5,(Uchar*)md5,mlen,(Uchar*)sig,slen,rsa); return ok; } static char *privPEM; static RSA *privRSA; static int pass_cb(char buf[],int size,int rwflag,void *userdata){ fprintf(stderr,"## Passphrase for KEY requested:%X\n",userdata); Xstrcpy(ZVStr(buf,size),(char*)userdata); return strlen((char*)userdata); } int SignRSA(PCStr(privkey),PCStr(data),PCStr(pass),PCStr(md5),int mlen,PVStr(sig),unsigned int *slen){ EVP_PKEY *pkey; RSA *rsa = 0; int ok; CStr(keybuff,8*1024); BIO *Bp; const char *cbdata = pass; if( checkCrypt() < 0 ){ return 0; } if( privRSA == NULL || privPEM == NULL || strcmp(privPEM,privkey)!=0 ){ OPENSSL_add_all_algorithms_conf(); /* !!!! mandatory !!!! */ if( data == 0 || *data == 0 ){ FILE *fp; int rcc; fp = fopen(privkey,"r"); if( fp == NULL ){ fprintf(stderr,"# %s: Can't open\n",privkey); return 0; } rcc = fread(keybuff,1,sizeof(keybuff)-1,fp); fclose(fp); if( rcc <= 0 ){ fprintf(stderr,"# %s: Can't read\n",privkey); return 0; } keybuff[rcc] = 0; data = keybuff; } Bp = BIO_new(BIO_s_mem()); BIO_puts(Bp,(char*)data); pkey = NULL; ERR_load_crypto_strings(); ENGINE_load_builtin_engines(); /* pkey = PEM_read_bio_PrivateKey(Bp,&pkey,pass_cb,cbdata); */ if( cbdata == 0 ) cbdata = ""; pkey = PEM_read_bio_PrivateKey(Bp,&pkey,NULL,cbdata); if( pkey == 0 ){ fprintf(stderr,"# %s: Can't load\n",privkey); return 0; } rsa = EVP_PKEY_get1_RSA(pkey); if( rsa == NULL ){ fprintf(stderr,"# %s: BAD KEY\n",privkey); return 0; } privPEM = strdup(privkey); privRSA = rsa; TRACE("loaded %s",privkey); } if( sig == NULL ){ return 1; } ok = signRSA(privRSA,md5,mlen,AVStr(sig),slen); return ok; } int verifyRSA(RSA *rsa,PCStr(md5),int mlen,PCStr(sig),unsigned int slen){ int siz; int ok = 0; if( checkCrypt() < 0 ) return 0; siz = RSA_size(rsa); ok = RSA_verify(NID_md5,(Uchar*)md5,mlen,(Uchar*)sig,slen,rsa); return ok; } static char *pubPEM; static RSA *pubRSA; int VerifyRSA(PCStr(pubkey),PCStr(data),PCStr(md5),int mlen,PCStr(sig),unsigned int slen){ X509 *x509; EVP_PKEY *pkey; RSA *rsa; int ok; CStr(keybuff,8*1024); BIO *Bp; if( checkCrypt() < 0 ) return 0; if( pubRSA == NULL || pubPEM == 0 || strcmp(pubPEM,pubkey) != 0 ){ if( data == 0 || *data == 0 ){ FILE *fp; int rcc; fp = fopen(pubkey,"r"); if( fp == NULL ){ fprintf(stderr,"# %s: Can't open\n",pubkey); return 0; } rcc = fread(keybuff,1,sizeof(keybuff)-1,fp); fclose(fp); if( rcc <= 0 ){ fprintf(stderr,"# %s: Can't read\n",pubkey); return 0; } keybuff[rcc] = 0; data = keybuff; } Bp = BIO_new(BIO_s_mem()); BIO_puts(Bp,(char*)data); pkey = NULL; x509 = PEM_read_bio_X509(Bp,NULL,NULL,NULL); if( x509 == NULL ){ fprintf(stderr,"# %s: BAD CERT\n",pubkey); return 0; } pubPEM = strdup(pubkey); pkey = X509_get_pubkey(x509); rsa = EVP_PKEY_get1_RSA(pkey); pubRSA = rsa; TRACE("loaded %s",pubkey); } if( sig == NULL ){ return 1; } ok = verifyRSA(pubRSA,md5,mlen,sig,slen); return ok; } static RSA *newRSApubkey(PCStr(key)){ BIO *Bp; X509 *x509; EVP_PKEY *pkey; RSA *rsa; Bp = BIO_new(BIO_s_mem()); BIO_puts(Bp,(char*)key); pkey = PEM_read_bio_PUBKEY(Bp,NULL,NULL,NULL); if( pkey == NULL ) return 0; rsa = EVP_PKEY_get1_RSA(pkey); return rsa; } int pubDecyptRSA(PCStr(pubkey),int len,PCStr(enc),PVStr(dec)){ RSA *rsa; int dlen; if( sslway_dl() <= 0 ) return -1; rsa = newRSApubkey(pubkey); if( rsa == NULL ){ return -1; } dlen = RSA_public_decrypt(len,(unsigned char*)enc,(unsigned char*)dec,rsa,RSA_PKCS1_PADDING); /*RSA_free(rsa);*/ return dlen; } #ifndef ISDLIB #include "randtext.c" #endif static double Start; static double laps[32]; static char *lapd[32]; static int lapx; #define Lap(msg) (loglevel> **issuer<<%s>>",what,sb,is); if( dp = (char*)strcasestr(sb,"/emailAddress=") ) wordscanY(dp+14,AVStr(ident),sizeof(ident),"^/"); else if( dp = (char*)strcasestr(sb,"/email=") ) wordscanY(dp+7,AVStr(ident),sizeof(ident),"^/"); else strcpy(ident,"someone"); X509_free(peer); }else{ TRACE("%s's cert. = NONE",what); strcpy(ident,"anonymous"); sb = ""; is = ""; } if( stdctl ){ fprintf(stdctl,"CFI/1.0 200- Ident:%s\r\n",ident); fprintf(stdctl,"CFI/1.0 200 Certificate:%s//%s\r\n",sb,is); fflush(stdctl); } } static void eRR_print_errors_fp(FILE *fp) { int code; const char *strp; CStr(str,1024); const char *file; const char *line; const char *opttxt; if( isWindows() ){ /* OpenSSL-0.9.7c on Win32 aborts in ERR_print_errors_fp() */ while( code = ERR_get_error() ){ strp = ERR_error_string_n(code,str,sizeof(str)); file = ""; line = ""; opttxt = ""; ERROR("SSL-ERRCODE: %X\r\n%d:%s:%s:%s:%s",code,getpid(), str,file,line,opttxt); } }else{ ERR_print_errors_fp(fp); } } #undef ERR_print_errors_fp #define ERR_print_errors_fp eRR_print_errors_fp static SSL *ssl_conn(SSL_CTX *ctx,int confd) { SSL *conSSL; Lap("ssl_conn() start"); conSSL = SSL_new(ctx); /* loadSessions(ctx,conSSL); */ loadSessions(ctx,conSSL,XCON); SSL_set_connect_state(conSSL); SSL_set_fd(conSSL,SocketOf(confd)); Lap("before connect"); if( SSL_connect(conSSL) < 0 ){ ERROR("connect failed"); ERR_print_errors_fp(stderr); if( SSL_fatalCB ){ (*SSL_fatalCB)("ssl_conn() failed\n"); } return NULL; }else{ Lap("after connect"); saveSessions(ctx,conSSL,XCON); TRACE("connected"); return conSSL; } } static SSL *ssl_acc(SSL_CTX *ctx,int accfd) { SSL *accSSL; Lap("ssl_acc() start"); /* loadSessions(ctx,NULL); */ loadSessions(ctx,NULL,XACC); accSSL = SSL_new(ctx); SSL_set_accept_state(accSSL); SSL_set_fd(accSSL,SocketOf(accfd)); SSL_set_fd(accSSL,SocketOf(accfd)); Lap("before accept"); if( SSL_accept(accSSL) < 0 ){ ERROR("accept failed"); ERR_print_errors_fp(stderr); /* 9.5.7 don't try writing to the non-established connection ssl_printf(accSSL,0,"SSLway: accept failed\n"); */ if( SSL_fatalCB ){ (*SSL_fatalCB)("ssl_acc() failed\n"); } return NULL; }else{ Lap("after accept"); saveSessions(ctx,accSSL,XACC); TRACE("accepted"); return accSSL; } } static void ssl_setCAs(SSL_CTX *ctx,PCStr(file),PCStr(dir)) { CStr(xfile,1024); CStr(xdir,1024); if( LIBFILE_IS(file,AVStr(xfile)) ) file = xfile; if( LIBFILE_IS(dir, AVStr(xdir)) ) dir = xdir; if( !SSL_CTX_load_verify_locations(ctx,file,dir) || !SSL_CTX_set_default_verify_paths(ctx) ){ if( SSL_fatalCB ){ (*SSL_fatalCB)("ssl_setCAs() failed\n"); } ERROR("CAs not found or wrong: [%s][%s]", file?file:"",dir?dir:""); }else{ TRACE("CAs = [%s][%s]",file?file:"",dir?dir:""); } } typedef struct { const char *c_cert; /* cetificate file */ const char *c_key; /* private key */ } CertKey1; typedef struct { CertKey1 v_ck[8]; /*QA(QC)*/ int v_Ncert; int v_Nkey; } CertKeyV; typedef struct { CertKeyV x_certkey; const char *x_pass; /* to decrypt the cert */ const char *x_CApath; /* CA's certificates */ const char *x_CAfile; /* A CA's certificate */ int x_do_SSL; /* use SSL */ int x_do_STLS; /* enable STARTTLS */ int x_nego_FTPDATA; int x_verify; int x_peeraddr; int x_sslver; int x_sslnover; } SSLContext; static const char sv_cert_default[] = "server-cert.pem"; static const char sv_key_default[] = "server-key.pem"; static const char sv_certkey_default[] = "server.pem"; static const char cl_cert_default[] = "client-cert.pem"; static const char cl_key_default[] = "client-key.pem"; static const char cl_certkey_default[] = "client.pem"; static const char *stls_proto; static SSLContext sslctx[2] = { { { {sv_cert_default, sv_key_default} } }, { { {cl_cert_default, cl_key_default} } }, }; static int acc_bareHTTP = 0; static int verify_depth = -1; static int do_showCERT = 0; static const char *cipher_list = NULL; #define sv_Cert sslctx[XACC].x_certkey #define sv_Ncert sslctx[XACC].x_certkey.v_Ncert #define sv_Nkey sslctx[XACC].x_certkey.v_Nkey #define sv_cert sslctx[XACC].x_certkey.v_ck[sv_Ncert].c_cert #define sv_key sslctx[XACC].x_certkey.v_ck[sv_Nkey].c_key #define sv_pass sslctx[XACC].x_pass #define cl_CApath sslctx[XACC].x_CApath #define cl_CAfile sslctx[XACC].x_CAfile #define do_accSSL sslctx[XACC].x_do_SSL #define do_accSTLS sslctx[XACC].x_do_STLS #define cl_vrfy sslctx[XACC].x_verify #define cl_nego_FTPDATA sslctx[XACC].x_nego_FTPDATA #define cl_addr sslctx[XACC].x_peeraddr #define cl_sslver sslctx[XACC].x_sslver #define cl_sslnover sslctx[XACC].x_sslnover #define cl_Cert sslctx[XCON].x_certkey #define cl_Ncert sslctx[XCON].x_certkey.v_Ncert #define cl_Nkey sslctx[XCON].x_certkey.v_Nkey #define cl_cert sslctx[XCON].x_certkey.v_ck[cl_Ncert].c_cert #define cl_key sslctx[XCON].x_certkey.v_ck[cl_Nkey].c_key #define cl_pass sslctx[XCON].x_pass #define sv_CApath sslctx[XCON].x_CApath #define sv_CAfile sslctx[XCON].x_CAfile #define do_conSSL sslctx[XCON].x_do_SSL #define do_conSTLS sslctx[XCON].x_do_STLS #define sv_vrfy sslctx[XCON].x_verify #define sv_nego_FTPDATA sslctx[XCON].x_nego_FTPDATA #define sv_addr sslctx[XCON].x_peeraddr #define sv_sslver sslctx[XCON].x_sslver #define sv_sslnover sslctx[XCON].x_sslnover #define ST_OPT 1 #define ST_FORCE 2 #define ST_AUTO 4 /* auto-detection of SSL by Client_Hello */ #define ST_SSL 8 /* AUTH SSL for FTP */ static SSL_CTX *ssl_new(int serv) { SSL_CTX *ctx; SSL_METHOD *meth; int sslver; int sslnover; SSL_library_init(); SSL_load_error_strings(); meth = 0; if( sslver = serv ? cl_sslver : sv_sslver ){ switch( sslver ){ case 1: if( serv ) meth = SSLv2_server_method(); else meth = SSLv2_client_method(); break; case 2: if( serv ) meth = SSLv3_server_method(); else meth = SSLv3_client_method(); break; case 3: if( serv ) meth = SSLv23_server_method(); else meth = SSLv23_client_method(); break; case 4: if( serv ) meth = TLSv1_server_method(); else meth = TLSv1_client_method(); break; } if( meth == 0 ){ ERROR("no method for %X",sslver); } } if( meth == 0 ){ if( serv ) meth = SSLv23_server_method(); else meth = SSLv23_client_method(); } ctx = SSL_CTX_new(meth); if( ctx ) if( sslnover = serv ? cl_sslnover : sv_sslnover ){ int opts = 0; switch( sslnover ){ case 1: opts |= SSL_OP_NO_SSLv2; break; case 2: opts |= SSL_OP_NO_SSLv3; break; case 3: opts |= SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3; break; } SSL_CTX_ctrl(ctx,SSL_CTRL_OPTIONS,opts,NULL); } return ctx; } static void passfilename(PCStr(keyfile),PVStr(passfile)) { refQStr(dp,passfile); /*QA(NL)*/ strcpy(passfile,keyfile); dp = strrchr(passfile,'.'); strcpy(dp,".pas"); } static int Freadline(PCStr(path),PVStr(line),int size) { FILE *fp; int rcc; const char *dp; fp = fopen(path,"r"); if( fp == NULL ) return -1; if( 0 < (rcc = fread((char*)line,1,QVSSize(line,size),fp)) ) setVStrEnd(line,rcc); else setVStrEnd(line,0); fclose(fp); if( dp = strpbrk(line,"\r\n") ) truncVStr(dp); return strlen(line); } static void scanpass(PCStr(arg)) { const char *file; CStr(path,1024); const char *pass; CStr(passb,128); if( strncmp(arg,"file:",5) == 0 ){ file = arg+5; if( LIBFILE_IS(file,AVStr(path)) ) file = path; passb[0] = 0; Freadline(file,AVStr(passb),sizeof(passb)); pass = passb; }else if( strncmp(arg,"pass:",5) == 0 ){ pass = arg + 5; }else{ ERROR("Usage: -pass { file:path | pass:string }"); return; } if( pass[0] ){ sv_pass = cl_pass = strdup(pass); } } unsigned int _inet_addrV4(PCStr(cp)); static void setaddrs(){ const char *env; cl_addr = 0; sv_addr = 0; if( env = getenv("REMOTE_ADDR") ){ cl_addr = _inet_addrV4(env); } if( env = getenv("SERVER_ADDR") ){ sv_addr = _inet_addrV4(env); } } static int saveContext(SSL_CTX *ctx,SSL *ssl,int ac,char *av[]){ X509 *cert; EVP_PKEY *ekey; unsigned char tmp[8*1024]; unsigned char *pp; /*QA(QS)*/ int len; FILE *fp; if( do_cache == 0 ) return -1; if( (do_cache & (1 << XCTX)) == 0 ) return -1; if( cert = SSL_get_certificate(ssl) ){ if( (ekey = SSL_get_privatekey(ssl)) ){ if( (cert = SSL_get_certificate(ssl)) ){ if( (fp = CFI_fopenShared("r+")) ){ fprintf(fp,"%s\n",CFI_FILTER_ID()); len = i2d_X509(cert,NULL); if( sizeof(tmp) <= len ){ ERROR("CERT too large %d/%d\n", len,sizeof(tmp)); return -1; } pp = tmp; len = i2d_X509(cert,&pp); fwrite(&len,1,sizeof(len),fp); fwrite(tmp,1,len,fp); len = i2d_PrivateKey(ekey,NULL); if( sizeof(tmp) <= len ){ ERROR("PKEY too large %d/%d\n", len,sizeof(tmp)); return -1; } pp = tmp; len = i2d_PrivateKey(ekey,&pp); fwrite(&len,1,sizeof(len),fp); fwrite(tmp,1,len,fp); fclose(fp); Lap("saveContext OK"); return 0; } } } } return -1; } static int loadContext(SSL_CTX *ctx,int ac,char *av[]){ FILE *fp; CStr(fid,64); const char *dp; X509 *cert; EVP_PKEY *pkey; unsigned CStr(buf,4096); unsigned char *pp; /*QA(RO)*/ int len; double start = Time(); if( do_cache == 0 ) return -1; if( (do_cache & (1 << XCTX)) == 0 ) return -1; fp = CFI_fopenShared("r"); if( fp == NULL ) return -1; /* if( CFI_sharedLock() != 0 ){ ERROR("ERROR: locking cache to load context failed"); goto CEXIT; } */ fgets(fid,sizeof(fid),fp); if( dp = strchr(fid,'\n') ) truncVStr(dp); if( strcmp(fid,CFI_FILTER_ID()) != 0 ){ goto CEXIT; } len = -1; fread(&len,1,sizeof(len),fp); if( len <= 0 || sizeof(buf) < len ){ ERROR("loadContext len=%d",len); goto CEXIT; } fread(buf,1,len,fp); pp = (unsigned char*)buf; cert = d2i_X509(NULL,&pp,len); len = -1; fread(&len,1,sizeof(len),fp); if( len <= 0 || sizeof(buf) < len ){ goto CEXIT; } fread(buf,1,len,fp); pp = (unsigned char*)buf; pkey = d2i_PrivateKey(EVP_PKEY_RSA,NULL,&pp,len); fclose(fp); if( SSL_CTX_use_certificate(ctx,cert) ){ if( SSL_CTX_use_PrivateKey(ctx,pkey) ){ if( SSL_CTX_check_private_key(ctx) ){ if( tlsdebug & DBG_SCACHE ){ fprintf(stderr,"[%d] %s loaded\n", getpid(),"CTX"); } Lap("loadContext OK"); return 0; } } } return -1; CEXIT: CFI_unLock(); fclose(fp); return -1; } typedef struct { int c_size; int c_nent; int c_pid; } SessionCache; typedef struct { unsigned char s_what; unsigned char s_flags; unsigned short s_leng; unsigned int s_date; unsigned int s_svaddr; /* IPv4 addr. of the server */ unsigned int s_claddr; /* IPv4 addr. of the client */ int s_ver; MStr(s_sid,32); } Session; /* using IP address is not good for a multi-homed server or a client * behind proxies ... */ #define SC_MAX 32 #define SC_ESIZE 2048 #define SC_BSIZE (SC_ESIZE-sizeof(Session)) #define SC_BASE 0x04000 #define SC_XBASE 0x10000 typedef struct { Session x_sess; unsigned MStr(x_buff,SC_BSIZE); } SessionCtx; static SessionCache scache; static Session sess_cache[SC_MAX]; static int sess_cached; static int sess_hits; static int loadScache(FILE *fp,int what){ int rcc; scache.c_nent = -1; scache.c_size = -1; scache.c_pid = 0; fseek(fp,SC_BASE,0); rcc = fread(&scache,1,sizeof(scache),fp); if( rcc != sizeof(scache) || scache.c_size <= 0 || sizeof(sess_cache) < scache.c_size ){ if( rcc != 0 ){ ERROR("ERROR[%s] bad session cache size:%X/%X[%X] %X/%d/%X", what==XACC?"FCL":"FSV", scache.c_nent,scache.c_size,scache.c_pid, SC_BASE,rcc,ftell(fp)); } return -1; } if( scache.c_pid == getpid() ){ /* needless to reload ... */ } rcc = fread(sess_cache,1,scache.c_size,fp); if( rcc < sizeof(Session) ){ return -1; } if( (rcc/sizeof(Session))*sizeof(Session) != rcc ){ return -1; } sess_cached = rcc / sizeof(Session); return sess_cached; } static void loadSessions(SSL_CTX *ctx,SSL *ssl,int what){ FILE *fp; double start = Time(); int nsess; int si; int ncon; int nacc; double Start; if( do_cache == 0 ) return; if( (do_cache & (1 << what)) == 0 ) return; Start = Time(); fp = CFI_fopenShared("r"); if( fp == NULL ){ return; } nacc = 0; ncon = 0; /* if( CFI_sharedLock() != 0 ){ ERROR("ERROR[%s] locking cache to load session failed", what==XACC?"FCL":"FSV"); goto CEXIT; } */ nsess = loadScache(fp,what); if( nsess <= 0 ) { goto CEXIT; } for( si = 0; si < nsess; si++ ){ Session *Sp; int cwhat; int len; int xlen; int rcc; SessionCtx scx; SSL_SESSION *sess; unsigned char *sp; /*QA(QC)*/ SSL_SESSION *s; Sp = &sess_cache[si]; cwhat = Sp->s_what; len = Sp->s_leng; xlen = sizeof(Session) + len; if( len <= 0 || sizeof(SessionCtx) < xlen ){ ERROR("loadSession[%d] FATAL len=%d",si,len); continue; } fseek(fp,SC_XBASE+si*SC_ESIZE,0); rcc = fread(&scx,1,xlen,fp); if( rcc != xlen ){ continue; } if( len != scx.x_sess.s_leng ){ continue; } if( bcmp(scx.x_sess.s_sid,Sp->s_sid,sizeof(Sp->s_sid)) != 0 ){ continue; } sp = (unsigned char*)scx.x_buff; s = SSL_SESSION_new(); sess = d2i_SSL_SESSION(&s,&sp,len); if( sess == 0 ){ continue; } if( what == XCON && cwhat == XCON ){ if( Sp->s_svaddr == sv_addr ) if( Sp->s_claddr == cl_addr ){ SSL_set_session(ssl,sess); ncon++; break; } }else if( what == XACC && cwhat == XACC ){ /* it should be callback ... */ if( Sp->s_svaddr == 0 || Sp->s_svaddr == sv_addr ) if( Sp->s_claddr == cl_addr ){ nacc++; SSL_CTX_add_session(ctx,sess); } } } CEXIT: CFI_unLock(); fclose(fp); if( ncon || nacc ){ Lap("loadSession OK"); }else{ Lap("loadSession NONE"); } ERROR("loadSession %.6f (%d %d) / %d",Time()-Start,ncon,nacc,nsess); } int strtoHex(PCStr(str),int len,PVStr(out),int siz); #define toHex(bi,sz,xb) strtoHex((char*)bi,sz,AVStr(xb),sizeof(xb)) static void saveSessions(SSL_CTX *ctx,SSL *ssl,int what){ FILE *fp; SSL_SESSION *sess; unsigned char *sp; /*QA(QS)*/ unsigned char sbuf[SC_BSIZE]; int len; double start; int si; int wcc; int nsess; int nsi; Session *Sp; SessionCtx scx; unsigned int oldest; int oi; SessionHead *shp; CStr(sib,128); int sc = sess_cached; if( do_cache == 0 ) return; if( (do_cache & (1 << what)) == 0 ){ return; } /* * don't save the session if currenst session is reused */ start = Time(); fp = CFI_fopenShared("r+"); if( fp == NULL ){ return; } if( CFI_exclusiveLock() != 0 ){ ERROR("ERROR[%s] locking cache to save session failed", what==XACC?"FCL":"FSV"); goto CEXIT; } loadScache(fp,what); sess = SSL_get_session(ssl); shp = (SessionHead*)sess; if( sess == NULL ){ ERROR("## no session to be saved"); goto CEXIT; } if( shp->ssl_version == 2 ){ DEBUG("## don't cache the session of SSL2"); goto CEXIT; } len = i2d_SSL_SESSION(sess,NULL); if( len == 0 ){ ERROR("## no session content to be saved"); goto CEXIT; } if( sizeof(sbuf) <= len ){ ERROR("## SESSION not saved (%d > %d)",len, sizeof(sbuf)); goto CEXIT; } sp = sbuf; len = i2d_SSL_SESSION(sess,&sp); if( SC_BSIZE < len ){ ERROR("Session not saved (DER Length: %d > %d)",len,SC_BSIZE); goto CEXIT; } oi = 0; oldest = 0xFFFFFFFF; if( sess_cached ){ for( si = 0; si < sess_cached; si++ ){ Sp = &sess_cache[si]; if( bcmp(shp->session_id,Sp->s_sid,shp->session_id_length)==0 ){ if( tlsdebug & DBG_SCACHE ){ toHex(Sp->s_sid,32,sib); Xstrcpy(DVStr(sib,40),"..."); fprintf(stderr,"[%d] %s scHIT %s\n", getpid(),what==XACC?"ACC":"CON",sib); } DEBUG("session HIT %X/%d/%d %X/%d", shp->ssl_version,shp->session_id_length,len, Sp->s_ver,Sp->s_leng); sess_hits++; goto CEXIT; } if( sess_cache[si].s_date < oldest ){ oldest = sess_cache[si].s_date; oi = si; } } } nsess = sess_cached; if( nsess == elnumof(sess_cache) ){ /* should search last used, least recently hit ... */ nsi = oi; }else{ nsi = nsess++; } Sp = &sess_cache[nsi]; Sp->s_leng = len; Sp->s_what = what; Sp->s_date = time(NULL); Sp->s_svaddr = sv_addr; Sp->s_claddr = cl_addr; Sp->s_ver = shp->ssl_version; Bcopy(shp->session_id,Sp->s_sid,shp->session_id_length); fseek(fp,SC_BASE,0); scache.c_nent = nsess; scache.c_size = nsess * sizeof(Session); scache.c_pid = getpid(); fwrite(&scache,1,sizeof(scache),fp); wcc = fwrite(sess_cache,1,scache.c_size,fp); fflush(fp); fseek(fp,SC_XBASE+nsi*SC_ESIZE,0); scx.x_sess = *Sp; Bcopy(sbuf,scx.x_buff,len); wcc = fwrite(&scx,1,sizeof(Session)+len,fp); fflush(fp); CFI_unLock(); if( tlsdebug & DBG_SCACHE ){ toHex((char*)shp->session_id,shp->session_id_length,sib); Xstrcpy(DVStr(sib,40),"..."); fprintf(stderr,"[%d] %s scPUT %s %d/%d\n", getpid(),what==XACC?"ACC":"CON",sib,wcc,nsess); } Lap("saveSession OK"); CEXIT: CFI_unLock(); fclose(fp); } static int setcert1(SSL_CTX *ctx,PCStr(certfile),PCStr(keyfile),int clnt) { int code = 0; CStr(cwd,1024); CStr(xkeyfile,1024); CStr(xcertfile,1024); const char *dp; CStr(passfile,1024); CStr(pass,128); if( LIBFILE_IS(keyfile,AVStr(xkeyfile)) ) keyfile = xkeyfile; if( LIBFILE_IS(certfile,AVStr(xcertfile)) ) certfile = xcertfile; pass[0] = 0; if( dp = strrchr(keyfile,'.') ){ passfilename(keyfile,AVStr(passfile)); if( 0 <= Freadline(passfile,AVStr(pass),sizeof(pass)) ){ if( clnt ) cl_pass = strdup(pass); else sv_pass = strdup(pass); } } getcwd(cwd,sizeof(cwd)); if( SSL_CTX_use_certificate_file(ctx,certfile,SSL_FILETYPE_PEM) ){ DEBUG("certfile loaded: %s",certfile); }else{ ERROR("certfile not found or wrong: %s [at %s]",certfile,cwd); code = -1; } if( SSL_CTX_use_RSAPrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM) ){ DEBUG("keyfile loaded: %s",keyfile); }else{ ERROR("keyfile not found or wrong: %s [at %s]",keyfile,cwd); code = -1; } if( !SSL_CTX_check_private_key(ctx) ){ ERROR("key does not match cert: %s %s",keyfile,certfile); code = -1; } return code; } int Ftruncate(FILE *fp,int offset,int whence); static int cert_opts; static const char *dflt_cert; static const char *dflt_vkey; void sslway_dflt_certkey(PCStr(cert),PCStr(vkey)){ dflt_cert = cert; dflt_vkey = vkey; } int set_dfltcerts(SSL_CTX *ctx){ X509 *cert; EVP_PKEY *pkey; CStr(file,128); int ok; BIO *Bp; if( dflt_cert == 0 || dflt_vkey == 0 ) return -1; Bp = BIO_new(BIO_s_mem()); BIO_puts(Bp,(char*)dflt_vkey); pkey = NULL; PEM_read_bio_RSAPrivateKey(Bp,&pkey,NULL,NULL); ok = SSL_CTX_use_RSAPrivateKey(ctx,pkey); if( !ok ){ ERROR("-- pkey=%X %d",pkey,ok); } BIO_puts(Bp,(char*)dflt_cert); cert = NULL; PEM_read_bio_X509(Bp,&cert,NULL,NULL); ok = SSL_CTX_use_certificate(ctx,cert); if( !ok ){ ERROR("-- cert=%X %d",cert,ok); } if( SSL_CTX_check_private_key(ctx) ){ ERROR("-- Using Default Certificate"); if( tlsdebug & DBG_XCACHE ){ fprintf(stderr,"[%d] %s using default\n", getpid(),"CTX"); } return 0; }else{ return -1; } } static int setcerts(SSL_CTX *ctx,CertKeyV *certv,int clnt) { int certx; int code; CertKey1 *cert1; for( certx = 0; certx <= certv->v_Ncert; certx++ ){ cert1 = &certv->v_ck[certx]; code = setcert1(ctx,cert1->c_cert,cert1->c_key,clnt); if( code != 0 ) { if( cert_opts == 0 ){ /* if not specified explicitly */ return set_dfltcerts(ctx); } if( SSL_fatalCB ){ (*SSL_fatalCB)("bad cert/key: [%s][%s]\n", cert1->c_cert,cert1->c_key); } return code; } } return 0; } static RSA *tmprsa_key; static RSA *tmprsa_callback(SSL *ctx,int exp,int bits) { if( bits != 512 && bits != 1024 ){ bits = 512; } if( tmprsa_key == NULL ){ tmprsa_key = RSA_generate_key(bits,0x10001,NULL,NULL); } return tmprsa_key; } static int verify_callback(int ok,X509_STORE_CTX *ctx) { int err,depth; const char *errsym; X509 *cert; CStr(subjb,256); cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); X509_NAME_oneline(X509_get_subject_name(cert),subjb,sizeof(subjb)); errsym = X509_verify_cert_error_string(err); ERROR("depth=%d/%d ok=%d %d:\"%s\" %s", depth,verify_depth,ok,err,errsym,subjb); if( !ok ){ if( depth <= verify_depth ) ok = 1; } return ok; } #define SSL_ERROR_WANT_READ 2 #define SSL_ERROR_WANT_WRITE 3 #define SSL_ERROR_WANT_X509_LOOKUP 4 static int SSL_rdwr(int wr,SSL *ssl,void *buf,int siz) { int i,xcc,err; if( wr ) xcc = SSL_write(ssl,buf,siz); else xcc = SSL_read(ssl,buf,siz); if( xcc < 0 ){ for( i = 0; i < 8; i++ ){ err = SSL_get_error(ssl,xcc); DEBUG("SSL_%s()=%d ERR=%d",wr?"write":"read",xcc,err); if( err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_X509_LOOKUP ) break; if( wr ) xcc = SSL_write(ssl,buf,siz); else xcc = SSL_read(ssl,buf,siz); if( 0 <= xcc ) break; } } return xcc; } #undef SSL_read #undef SSL_write #define SSL_read(ss,bf,sz) SSL_rdwr(0,ss,bf,sz) #define SSL_write(ss,bf,sz) SSL_rdwr(1,ss,(char*)bf,sz) static void writes(PCStr(what),SSL *ssl,int confd,void *buf,int rcc) { int wcc,rem; rem = rcc; while( 0 < rem ){ if( ssl ) wcc = SSL_write(ssl,buf,rem); else wcc = write(confd,buf,rem); if( wcc == rem ) DEBUG("%s: %d/%d -> %d%s",what,rem,rcc,wcc,ssl?"/SSL":""); else ERROR("%s? %d/%d -> %d%s",what,rem,rcc,wcc,ssl?"/SSL":""); if( wcc <= 0 ) break; rem -= wcc; } } static int nego_FTPDATAsv(SSL *accSSL,char buf[],int len); static void nego_FTPDATAcl(SSL *conSSL,char sbuf[],int len); static void ssl_relay(SSL *accSSL,int accfd,SSL *conSSL,int confd) { int fdv[2],rfdv[2],nready,rcc,wcc; CStr(buf,8*1024); fdv[0] = accfd; fdv[1] = confd; if( cl_nego_FTPDATA ) nego_FTPDATAcl(conSSL,"",0); for(;;){ nready = 0; rfdv[0] = rfdv[1] = 0; if( accSSL && SSL_pending(accSSL) ){ rfdv[0] = 1; nready++; } if( conSSL && SSL_pending(conSSL) ){ rfdv[1] = 1; nready++; } if( nready == 0 ){ nready = PollIns(0,2,fdv,rfdv); if( nready <= 0 ) break; } if( rfdv[0] ){ if( accSSL ) rcc = SSL_read(accSSL,buf,sizeof(buf)); else rcc = read(accfd,buf,sizeof(buf)); if( rcc <= 0 ) { TRACE("C-S EOF from the client"); break; } if( sv_nego_FTPDATA ) rcc = nego_FTPDATAsv(accSSL,buf,rcc); writes("C-S",conSSL,confd,buf,rcc); } if( rfdv[1] ){ if( conSSL ) rcc = SSL_read(conSSL,buf,sizeof(buf)); else rcc = read(confd,buf,sizeof(buf)); if( rcc <= 0 ) { TRACE("S-C EOF from the server"); break; } if( cl_nego_FTPDATA ) nego_FTPDATAcl(conSSL,buf,rcc); writes("S-C",accSSL,accfd,buf,rcc); } } } /* * STARTTLS: * RFC2487 SMTP * RFC2595 IMAP and POP3 * RFC2228,draft-murray-auth-ftp-ssl-07 FTP */ static char *relay_opening(PCStr(proto),FILE *fs,FILE *tc,PVStr(buf),int bsize) { CStr(msgb,1024); for(;;){ if( fgets(buf,bsize,fs) == NULL ) return NULL; fputs(buf,tc); fflush(tc); if( proto != NULL ) break; if( strncmp(buf,"220",3) == 0 ){ if( buf[3] == '-' ){ do { fgets(msgb,sizeof(msgb),fs); fputs(msgb,tc); } while( msgb[3] == '-' ); fflush(tc); } if( strstr(buf,"FTP") ) proto = "FTP"; else proto = "SMTP"; break; }else if( strncasecmp(buf,"+OK",3) == 0 ){ proto = "POP3"; break; }else if( strncasecmp(buf,"* OK",4) == 0 ){ proto = "IMAP"; break; }else{ return NULL; } } return (char*)proto; } static int isinSSL(int fd) { unsigned char buf[6]; /*QA(QF)*/ int rcc,leng,type,vmaj,vmin; buf[0] = 0x7F; RecvPeek(fd,buf,1); if( (buf[0] & 0x80) || buf[0] < 0x20 ){ ERROR("STARTTLS got binary [%X] from client",0xFF&buf[0]); if( buf[0] == 0x80 ){ rcc = RecvPeek(fd,buf,5); ERROR("SSL Hello?%d [%X %d %d %d %d]",rcc,buf[0], buf[1],buf[2],buf[3],buf[4]); leng = (0x7F&buf[0]) << 8 | buf[1]; type = buf[2]; if( type == 1 ){ /* SSLv3 ClientHello */ vmaj = buf[3]; vmin = buf[4]; return 1; } } else if( buf[0] == 22 ){ /* ConentType:handshake */ rcc = RecvPeek(fd,buf,sizeof(buf)); ERROR("SSL Hello?%d [%X %d %d %d %d]",rcc,buf[0], buf[1],buf[2],buf[3]<<8|buf[4],buf[5]); if( buf[5] == 1 ){ return 1; } } } return 0; } static int starttls(int accfd,int confd) { FILE *fc,*tc,*fs,*ts; int fdv[2],rfdv[2]; CStr(buf,1024); CStr(com,32); CStr(arg,32); const char *msg; CStr(msgb,1024); const char *dp; const char *proto; int xi; fdv[0] = accfd; fdv[1] = confd; fc = fdopen(fdv[0],"r"); setbuf(fc,NULL); tc = fdopen(fdv[0],"w"); fs = fdopen(fdv[1],"r"); setbuf(fs,NULL); ts = fdopen(fdv[1],"w"); proto = stls_proto; if( do_conSSL && do_conSTLS ){ proto = relay_opening(proto,fs,tc,AVStr(buf),sizeof(buf)); if( proto == NULL ) return -1; ERROR("STARTTLS to server -- %s",proto); if( strcasecmp(proto,"FTP") == 0 ){ if( do_conSTLS & ST_SSL ) fputs("AUTH SSL\r\n",ts); else{ fputs("AUTH TLS\r\n",ts); if( do_conSTLS & ST_FORCE ) cl_nego_FTPDATA = 1; } }else if( strcasecmp(proto,"SMTP") == 0 ){ fputs("STARTTLS\r\n",ts); }else if( strncasecmp(proto,"POP",3) == 0 ){ fputs("STLS\r\n",ts); }else if( strcasecmp(proto,"IMAP") == 0 ){ fputs("stls0 STARTTLS\r\n",ts); } fflush(ts); if( fgets(buf,sizeof(buf),fs) == NULL ) return -1; if( dp = strpbrk(buf,"\r\n") ) truncVStr(dp); ERROR("STARTTLS to server -- %s",buf); } if( do_accSSL && do_accSTLS ){ for( xi = 0; ; xi++ ){ PollIns(0,2,fdv,rfdv); if( rfdv[0] ){ if( xi == 0 /* && accept implicit SSL too */ ){ if( isinSSL(fdv[0]) ) return 0; if( do_accSTLS == ST_AUTO ){ ERROR("SSL-autodetect C-S: not in SSL"); do_accSSL = 0; return 0; } } if( fgets(buf,sizeof(buf),fc) == NULL ) return -1; dp = wordscanX(buf,AVStr(com),sizeof(com)); wordscanX(dp,AVStr(arg),sizeof(arg)); ERROR("STARTTLS prologue: C-S: [%s][%s]",com,arg); /* SMTP */ if( strcasecmp(com,"EHLO") == 0 ){ write(accfd,"250 STARTTLS\r\n",14); continue; } if( strcasecmp(com,"STARTTLS") == 0 ){ msg = "220 Ready to start TLS\r\n"; write(accfd,msg,strlen(msg)); ERROR("STARTTLS from SMTP client -- OK"); break; } /* POP3 */ if( strcasecmp(com,"STLS") == 0 ){ msg = "+OK Begin TLS negotiation\r\n"; write(accfd,msg,strlen(msg)); ERROR("STARTTLS from POP client -- OK"); break; } /* IMAP */ if( strcasecmp(arg,"CAPABILITY") == 0 ){ msg = "* CAPABILITY STARTTLS\r\n"; write(accfd,msg,strlen(msg)); sprintf(msgb,"%s OK CAPABILITY\r\n",com); write(accfd,msgb,strlen(msgb)); continue; } if( strcasecmp(arg,"STARTTLS") == 0 ){ sprintf(msgb,"%s OK Begin TLS negotiation\r\n",com); write(accfd,msgb,strlen(msgb)); ERROR("STARTTLS from IMAP client -- OK"); break; } /* FTP */ if( strcasecmp(com,"AUTH") == 0 ) if( strcasecmp(arg,"TLS") == 0 || strcasecmp(arg,"SSL") == 0 ){ msg = "234 OK\r\n"; write(accfd,msg,strlen(msg)); ERROR("AUTH %s from FTP client -- 234 OK",arg); if( strcasecmp(arg,"TLS") == 0 && do_accSTLS == ST_FORCE ) sv_nego_FTPDATA = 1; break; } /* HTTP */ if( strcasecmp(com,"CONNECT") == 0 ){ if( proto == 0 ){ proto = "http"; } } if( do_accSTLS == 2 ){ ERROR("STARTTLS required"); if( proto != 0 && strcasecmp(proto,"IMAP") == 0 ) fprintf(tc,"%s BAD do STARTTLS first.\r\n",com); else if( proto != 0 && strcasecmp(proto,"POP") == 0 ) fprintf(tc,"+ERR do STLS first.\r\n"); else fprintf(tc,"530 do STARTTLS first.\r\n"); fflush(tc); return -1; } fputs(buf,ts); fflush(ts); } if( rfdv[1] ){ if( xi == 0 ){ if( isinSSL(fdv[1]) ) /* will not match */ return 0; if( do_accSTLS == ST_AUTO ){ ERROR("SSL-autodetect S-C: not in SSL"); do_accSSL = 0; return 0; } } if( proto == NULL ){ proto = relay_opening(proto,fs,tc,AVStr(buf),sizeof(buf)); if( proto == NULL ) return -1; ERROR("STARTTLS to client -- %s",proto); }else{ if( fgets(buf,sizeof(buf),fs) == NULL ) return -1; fputs(buf,tc); } /* HTTP */ if( proto != NULL && streq(proto,"http") ){ if( buf[0] == '\r' || buf[1] == '\n' ){ ERROR("STARTTLS prologue: S-C HTTP-CONNECT DONE"); fflush(tc); break; } } if( dp = strpbrk(buf,"\r\n") ) truncVStr(dp); ERROR("STARTTLS prologue: S-C: %s",buf); fflush(tc); } } } return 0; } static int nego_FTPDATAsv(SSL *accSSL,char buf[],int len) { CStr(com,32); CStr(arg,32); const char *dp; const char *msg; buf[len] = 0; dp = wordscanX(buf,AVStr(com),sizeof(com)); wordscanX(dp,AVStr(arg),sizeof(arg)); if( strcasecmp(com,"PBSZ") == 0 ){ msg = "200 OK\r\n"; SSL_write(accSSL,msg,strlen(msg)); ERROR("PBSZ %s from FTP client -- 200 OK",arg); len = 0; } else if( strcasecmp(com,"PROT") == 0 ){ msg = "200 OK\r\n"; SSL_write(accSSL,msg,strlen(msg)); ERROR("PROT %s from FTP client -- 200 OK",arg); len = 0; sv_nego_FTPDATA = 0; } return len; } #define FTP_LOGIN_OK "230" static void nego_FTPDATAcl(SSL *conSSL,char sbuf[],int len) { const char *msg; CStr(buf,64); CStr(resp,64); int rcc; if( len != 0 ) if( strncmp(sbuf,FTP_LOGIN_OK,strlen(FTP_LOGIN_OK)) != 0 ) return; msg = "PBSZ 0\r\n"; SSL_write(conSSL,msg,strlen(msg)); if( 0 <= (rcc = SSL_read(conSSL,buf,sizeof(buf)-1)) ) setVStrEnd(buf,rcc); else setVStrEnd(buf,0); linescanX(buf,AVStr(resp),sizeof(resp)); ERROR("STARTTLS/FTP PBSZ 0 -> %s",resp); if( atoi(resp) != 200 ) return; msg = "PROT P\r\n"; SSL_write(conSSL,msg,strlen(msg)); if( 0 <= (rcc = SSL_read(conSSL,buf,sizeof(buf)-1)) ) setVStrEnd(buf,rcc); else setVStrEnd(buf,0); linescanX(buf,AVStr(resp),sizeof(resp)); ERROR("STARTTLS/FTP PROT P -> %s",resp); if( atoi(resp) == 200 ) cl_nego_FTPDATA = 0; } static int HTTP_CAresp(int fd,PCStr(certfile)) { FILE *tc,*cfp; X509 *cert; BIO *in,*out; tc = fdopen(fd,"w"); cfp = fopen(certfile,"r"); if( cfp == NULL ) return -1; fprintf(tc,"HTTP/1.0 200 ok\r\n"); fprintf(tc,"MIME-Version: 1.0\r\n"); fprintf(tc,"Content-Type: application/x-x509-ca-cert\r\n"); fprintf(tc,"\r\n"); in = BIO_new_fp(cfp,BIO_NOCLOSE); cert = PEM_read_bio_X509(in,NULL,NULL,NULL); out = BIO_new_fp(tc,BIO_NOCLOSE); i2d_X509_bio(out,cert); BIO_free(in); BIO_free(out); fclose(tc); return 0; } static int CArequest(int accfd,int *isHTTP,PCStr(certfile)) { CStr(method,8); CStr(line,1024); CStr(url,1024); int rcc; setNonblockingIO(accfd,1); rcc = RecvPeek(accfd,method,6); setNonblockingIO(accfd,0); if( rcc <= 0 ) return 0; setVStrEnd(method,rcc); if( strncmp(method,"GET ",4) == 0 ){ setNonblockingIO(accfd,1); rcc = RecvPeek(accfd,line,16); setNonblockingIO(accfd,0); setVStrEnd(line,rcc); wordscanX(line+4,AVStr(url),sizeof(url)); if( strcmp(url,"/-/ca.der") == 0 ){ HTTP_CAresp(0,certfile); TRACE("sent cert"); return 1; } *isHTTP = 1; }else if( strncmp(method,"HEAD ",5) == 0 || strncmp(method,"POST ",5) == 0 ) *isHTTP = 1; return 0; } static void rand_seed() { int seed[8],si; seed[0] = Gettimeofday(&seed[1]); RAND_seed(seed,sizeof(int)*2); /* seed[2] = getpid(); seed[3] = getuid(); seed[4] = (int)seed; seed[5] = (int)rand_seed; RAND_seed(seed,sizeof(seed)); */ for( si = 0; si < 8; si++ ) seed[si] = 0; } int (*SSL_getpassCB)(PCStr(file),PVStr(pass),int size); static int _passwd(PCStr(what),PCStr(pass),PCStr(keyfile),char buf[],int siz,int vrfy) { CStr(passfile,1024); if( pass ){ TRACE("passphrase for %s -- OK",keyfile); Xstrcpy(ZVStr(buf,siz),pass); return strlen(pass); }else if( SSL_getpassCB && (*SSL_getpassCB)(keyfile,ZVStr(buf,siz),siz)==0 ){ TRACE("passphrase CB for %s -- OK",keyfile); return strlen(buf); }else{ passfilename(keyfile,AVStr(passfile)); ERROR("passphrase for %s -- ERROR: '%s' file not found and SSL_%s_KEY_PASSWD undefined", keyfile,passfile,what); return -1; } } static int sv_passwd(char buf[],int siz,int vrfy) { return _passwd("SERVER",sv_pass,sv_key,buf,siz,vrfy); } static int cl_passwd(char buf[],int siz,int vrfy) { return _passwd("CLIENT",cl_pass,cl_key,buf,siz,vrfy); } static SSL_SESSION *get_session_cb(SSL *ssl,unsigned char *id,int len,int *copy){ DEBUG("--CB-- GET SESSION %d [%2X]",len,id[0]); return 0; } static int new_session_cb(SSL *ssl,SSL_SESSION *sess){ int len; len = i2d_SSL_SESSION(sess,NULL); DEBUG("--CB-- NEW SESSION CREATED %X, len=%d",sess,len); return 0; } static int gen_session_cb(const SSL *ssl,unsigned char *id,unsigned int *id_len){ int i; DEBUG("--CB-- GEN SESSION CB, len=%d",*id_len); return 1; /* for(i = 0; i < *id_len; i++) fprintf(stderr," %02X",id[i]); fprintf(stderr,"\n"); id[0] = 'X'; *id_len = 1; return 1; */ } static void put_help() { syslog_ERROR("SSLway 2006-11-13 \r\n"); syslog_ERROR("SSLlib %s\r\n",SSLeay_version(0)); } int sslway_mainX(int ac,char *av[],int client,int server,int bi) { int ai; const char *arg; int accfd,confd; SSL_CTX *ctx; SSL *accSSL,*conSSL; const char *env; int fdv[2],rfdv[2]; int vflag; int ctrlopt = 0; int vflags = 0; X509_STORE *store; int nodelay = 1; int fid = -1; int ctx_reuse = 0; int sreused = 0; int sync = -1; /* global variables shared and reused via dynamic linking ... */ { do_accSSL = 0; do_conSSL = 0; } Start = Time(); lapx = 0; setaddrs(); for( ai = 1; ai < ac; ai++ ){ arg = av[ai]; if( strcmp(arg,"-help") == 0 || strcmp(arg,"-v") == 0 ){ put_help(); exit(0); }else if( strncmp(arg,"-va",3) == 0 ){ int aj; for( aj = 0; aj < ac; aj++ ) ERROR("arg[%d] %s",aj,av[aj]); }else{ opt1(arg); } } Lap("start"); nthcall++; if( env = getenv("CFI_FILTER_ID") ) fid = atoi(env); /* if( !bi ) CFI_SHARED_FD or so is necessary */ CFI_init(ac,(const char**)av); Lap("init done"); if( env = getenv("CFI_TYPE") ){ if( strcmp(env,"FCL") == 0 ){ DEBUG("CFI_TYPE=%s: -ac is assumed",env); do_accSSL = 1; }else if( strcmp(env,"FSV") == 0 || strcmp(env,"FMD") == 0 ){ DEBUG("CFI_TYPE=%s: -co is assumed",env); do_conSSL = 1; } } if( env = getenv("CFI_STAT") ){ int fd; fd = atoi(env); stdctl = fdopen(fd,"w"); fprintf(stdctl,"CFI/1.0 100 start\r\n"); fflush(stdctl); } if( env = getenv("CFI_SYNC") ){ sync = atoi(env); if( 0 <= sync ){ TRACE("CFI_SYNC send start [%d]",sync); write(sync,"W",1); } } if( env = getenv("SSL_KEY_PASSWD") ) sv_pass = cl_pass = env; if( env = getenv("SSL_CLIENT_KEY_PASSWD") ) cl_pass = env; if( env = getenv("SSL_SERVER_KEY_PASSWD") ) sv_pass = env; PID = getpid(); if( (client_host = getenv("REMOTE_HOST")) == 0 ) client_host = "?"; /* accfd = dup(0); confd = dup(1); */ if( 0 <= client ){ accfd = dup(client); confd = dup(server); }else{ accfd = -1; confd = -1; } if( env = getenv("SSL_CIPHER") ) cipher_list = env; if( env = getenv("SSL_CERT_FILE") ) sv_cert = sv_key = cl_cert = cl_key = env; if( env = getenv("SSL_SERVER_KEY_FILE" ) ) sv_key = env; if( env = getenv("SSL_SERVER_CERT_FILE") ) sv_cert = env; if( env = getenv("SSL_CLIENT_KEY_FILE" ) ) cl_key = env; if( env = getenv("SSL_CLIENT_CERT_FILE") ) cl_cert = env; Lap("begin args"); for( ai = 1; ai < ac; ai++ ){ arg = av[ai]; if( strcmp(arg,"-help") == 0 || strcmp(arg,"-v") == 0 ){ }else if( strncmp(arg,"-vv",3) == 0 || strncmp(arg,"-vd",3) == 0 ){ }else if( strncmp(arg,"-vu",3) == 0 ){ }else if( strncmp(arg,"-vt",3) == 0 ){ }else if( strncmp(arg,"-vs",3) == 0 ){ }else if( strneq(arg,"-no_ssl",7) ){ int sslnover = 0; if( streq(arg+7,"2") ) sslnover = 1; else if( streq(arg+7,"3") ) sslnover = 2; else if( streq(arg+7,"23")) sslnover = 3; sv_sslnover = cl_sslnover = sslnover; }else if( strneq(arg,"-ssl",4) ){ int sslver = 0; if( streq(arg+4,"2") ) sslver = 1; else if( streq(arg+4,"3") ) sslver = 2; else if( streq(arg+4,"23")) sslver = 3; sv_sslver = cl_sslver = sslver; }else if( strneq(arg,"-tls",4) ){ int sslver = 0; if( streq(arg+4,"1") ) sslver = 4; sv_sslver = cl_sslver = sslver; }else if( strncasecmp(arg,"-ss",3) == 0 ){ do_accSTLS = do_conSTLS = ST_SSL; if( arg[3] == '/' && arg[4] != 0 ) stls_proto = strdup(arg+4); }else if( strncasecmp(arg,"-st",3) == 0 ){ do_accSTLS = do_conSTLS = arg[1]=='s'?1:2; if( arg[3] == '/' && arg[4] != 0 ) stls_proto = strdup(arg+4); }else if( strncasecmp(arg,"-ad",3) == 0 ){ do_accSTLS = do_conSTLS = ST_AUTO; }else if( strncmp(arg,"-ac",3) == 0 ){ do_accSSL = 1; if( strncmp(arg+3,"/st",3) == 0 ) do_accSTLS = 1; }else if( strncmp(arg,"-co",3) == 0 ){ do_conSSL = 1; if( strncmp(arg+3,"/st",3) == 0 ) do_conSTLS = 1; }else if( strncmp(arg,"-ht",3) == 0 ){ acc_bareHTTP = 1; }else if( strncmp(arg,"-show",3) == 0 ){ do_showCERT = 1; }else if( strcmp(arg,"-CApath") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s directory-name",arg); return -1; } cl_CApath = sv_CApath = av[++ai]; }else if( strcmp(arg,"-CAfile") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s file-name",arg); return -1; } cl_CAfile = sv_CAfile = av[++ai]; }else if( strcasecmp(arg,"-vrfy")==0 || strcasecmp(arg,"-auth")==0 ){ vflag = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; if( arg[1] == 'V' || arg[1] == 'A' ) vflag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if( arg[1] == 'V' || arg[1] == 'v' ) verify_depth = -1; else verify_depth = 10; cl_vrfy = vflag; sv_vrfy = vflag; }else if( strcasecmp(arg,"-verify") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s max-depth",arg); return -1; } verify_depth = atoi(av[++ai]); vflag = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; if( arg[1] == 'V' ) vflag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; cl_vrfy = vflag; sv_vrfy = vflag; }else if( strcmp(arg,"-client_auth") == 0 ){ verify_depth = 10; cl_vrfy = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; }else if( strcmp(arg,"-cipher") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s cipher-list",arg); return -1; } cipher_list = av[++ai]; }else if( strcmp(arg,"-certkey") == 0 || strcmp(arg,"-cert") == 0 ){ cert_opts++; if( ac <= ai + 1 ){ ERROR("Usage: %s cert-key-file-name",arg); return -1; } if( sv_cert != sv_cert_default ){ if( elnumof(sv_Cert.v_ck) <= sv_Ncert+1 ){ }else{ sv_Ncert++; sv_Nkey++; } } if( cl_cert != cl_cert_default ){ if( elnumof(cl_Cert.v_ck) <= cl_Ncert+1 ){ }else{ cl_Ncert++; cl_Nkey++; } } sv_cert = sv_key = cl_cert = cl_key = av[++ai]; } else if( strcmp(arg,"-key") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s key-file-name",arg); return -1; } sv_key = cl_key = av[++ai]; } else if( strcmp(arg,"-pass") == 0 ){ if( ac <= ai + 1 ){ ERROR("Usage: %s {pass:str|file:path}"); return -1; } scanpass(av[++ai]); } else if( strcmp(arg,"-bugs") == 0 ){ ctrlopt = 0x000FFFFFL; /* SSL_OP_ALL */ } else if( strcmp(arg,"-nocache") == 0 ){ do_cache = 0; } else if( strcmp(arg,"-delay") == 0 ){ nodelay = 0; } else if( strcmp(arg,"-crl_check") == 0 ){ vflags |= X509_V_FLAG_CRL_CHECK; } else if( strcmp(arg,"-crl_check_all") == 0 ){ vflags |= X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL; } } Lap("end args"); accSSL = NULL; conSSL = NULL; if( do_conSSL || do_accSSL ) { if( nthcall <= 1 ){ rand_seed(); Lap("end rand_seed"); } TRACE("start"); } if( accfd < 0 ){ /* setting ctx */ }else{ if( nodelay ){ set_nodelay(accfd,1); set_nodelay(confd,1); Lap("nodelay set"); } fdv[0] = accfd; fdv[1] = confd; if( acc_bareHTTP ){ int isHTTP; if( 0 < PollIns(100,2,fdv,rfdv) && 0> shutdown from server: %X",sd); if( sd & SSL_RECEIVED_SHUTDOWN ){ TRACE("S<< return shutdown to server"); SSL_shutdown(conSSL); } } if( accSSL ){ int sd = SSL_get_shutdown(accSSL); TRACE("C>> shutdown from client: %X",sd); if( sd & SSL_RECEIVED_SHUTDOWN ){ TRACE("C<< return shutdown to client"); SSL_shutdown(accSSL); } } if( do_conSSL || do_accSSL ) TRACE("done"); return 0; } #ifdef ISDLIB /*{*/ int dl_library(const char *libname,DLMap *dlmap,const char *mode); static int with_dl; /* int sslway_dl(){ */ static int sslway_dl0(){ if( with_dl ){ if( 0 < with_dl ) return 1; else return 0; } if( SSLLIBS ){ CStr(lib1,1024); const char *dp; char del = '+'; for( dp = SSLLIBS; *dp; ){ dp = scan_ListElem1(dp,del,AVStr(lib1)); if( *lib1 == 0 ) break; if( lDYLIB() ) syslog_ERROR("TSLCONF=libs:%s\n",lib1); if( streq(lib1,"NOMORE") ){ with_dl = -1; return 0; } if( dl_library(lib1,dlmap_ssl,"") == 0 ){ with_dl = 1; return 1; } } } #ifdef _MSC_VER if( dl_library("ssl",dlmap_ssl,"") == 0 || dl_library("libeay32",dlmap_ssl,"") == 0 || dl_library("ssleay32",dlmap_ssl,"") == 0 ){ with_dl = 1; return 1; }else #else if( dl_library("ssl",dlmap_ssl,"") == 0 || dl_library("crypto",dlmap_ssl,"") == 0 ){ with_dl = 1; return 1; }else #endif { with_dl = -1; if( !lISCHILD() ){ putDylibError(); } return 0; } } int sslway_dl(){ int ok; if( with_dl ){ return sslway_dl0(); }else if( ok = sslway_dl0() ){ InitLog("+++ loaded %s\n",SSLeay_version(0)); if( lDYLIB() ) printf("+++ loaded %s\n",SSLeay_version(0)); return ok; }else{ return 0; } } int putSSLverX(FILE *fp,PCStr(fmt)){ if( with_dl <= 0 ) return 0; fprintf(fp,"%s",SSLeay_version(0)); return 1; } void putSSLver(FILE *fp){ if( 0 < with_dl ) fprintf(fp,"Loaded: %s\r\n",SSLeay_version(0)); } int sslway_main(int ac,const char *av[]) { if( sslway_dl() == 0 ){ fprintf(stderr,"Can't link the SSL library.\n"); return -1; } Builtin = 1; return sslway_mainX(ac,(char**)av,0,1,0); } int sslwayFilter(int ac,char *av[],FILE *in,FILE *out,int internal){ if( sslway_dl() == 0 ){ return 0; } Builtin = 1; if( in == NULL ) sslway_mainX(ac,av,-1,-1,1); else sslway_mainX(ac,av,fileno(in),fileno(out),1); return 1; } #else /*}{*/ int (*DELEGATE_MAIN)(int ac,const char *av[]); void (*DELEGATE_TERMINATE)(); extern int RANDSTACK_RANGE; int main(int ac,char *av[]) { randtext(-1); RANDSTACK_RANGE = 256; av = move_envarg(ac,(const char**)av,NULL,NULL,NULL); return randstack_call(1,(iFUNCP)sslway_mainX,ac,av,0,1,0); } int sslway_dl(){ return 1; } #endif /*}*/