/* ssl-auth.c, part of netpipes: network pipe utilities Copyright (C) 1997-98 Robert Forsman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static char info[] = "ssl-auth: a utility for sockets\nWritten 1997-98 by Robert Forsman \n"; /* Ssl-Auth ssl-auth is designed to be used in a script spawned as the child of a faucet or hose. ssl-auth is a program to encrypt a conversation over a socket connection. The data arriving on stdin is encrypted and written to the socket. Data arriving on the socket is decrypted and sent to stdout. A subprocess may be spawned and looped through the socket. ssl-auth should also be able to verify the identity of the other end of the socket, and also identify itself to the other end. In certain applications, it is desirable to accept a connection from anyone with a certificate from an authority. */ #include #include extern int errno; /* I hate the errno header file */ #include #include #include #include #include #include #include #include #include #include "common.h" #include "memmove.h" #include #include #include #include #include #include "ssl-criteria.h" #define EXITCODE_ARGS 127 #define EXITCODE_SYSCALLFAILED 126 #define EXITCODE_EXECFAILED 125 #define EXITCODE_SIGNAL 124 #define EXITCODE_SSL_ERROR 123 #define PACKETCODE_EOF 0 #define PACKETCODE_EOF_WAITING 1 #define PACKETCODE_EXITSTATUS 2 static int subproc=0; static int verbose=0; /********************************************************************/ static void usage() { fprintf(stderr, "This is out of date\n"); fprintf(stderr, "Usage : %s --fd n [ --verbose ] [ --subproc [ --infd n ] [ --outfd n ] ]\n\ [ --verify n ] [ --cipher cipher ] [ --server | --client ]\n\ [ --cert certfile ] [ --key keyfile ] [ --CApath path ] [ --CAfile path ]\n\ [ --criteria criteria-list ]\n\ -[#n][v][s[in][on]] ]\n\ --server and --client are mutually exclusive.\n", progname); } /********************************************************************/ BIO *bio_err = 0; BIO *bio_c_out=0; int verify_depth=0; int verify_error=X509_V_OK; int set_cert_stuff(ctx, cert_file, key_file) SSL_CTX *ctx; char *cert_file; char *key_file; { if (cert_file != NULL) { SSL *ssl; X509 *x509; if (SSL_CTX_use_certificate_file(ctx,cert_file, SSL_FILETYPE_PEM) <= 0) { BIO_printf(bio_err,"unable to set certificate file\n"); ERR_print_errors(bio_err); return(0); } if (key_file == NULL) key_file=cert_file; if (SSL_CTX_use_PrivateKey_file(ctx,key_file, SSL_FILETYPE_PEM) <= 0) { BIO_printf(bio_err,"unable to set public key file\n"); ERR_print_errors(bio_err); return(0); } ssl=SSL_new(ctx); x509=SSL_get_certificate(ssl); if (x509 != NULL) EVP_PKEY_copy_parameters(X509_get_pubkey(x509), SSL_get_privatekey(ssl)); SSL_free(ssl); /* If we are using DSA, we can copy the parameters from * the private key */ /* Now we know that a key and cert have been set against * the SSL context */ if (!SSL_CTX_check_private_key(ctx)) { BIO_printf(bio_err,"Private key does not match the certificate public key\n"); return(0); } } return(1); } int verify_callback(ok, ctx) int ok; X509_STORE_CTX *ctx; { char buf[256]; X509 *err_cert; int err,depth; err_cert=X509_STORE_CTX_get_current_cert(ctx); err= X509_STORE_CTX_get_error(ctx); depth= X509_STORE_CTX_get_error_depth(ctx); if (!ok) { int pseudo_depth; switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: pseudo_depth = depth+1; break; default: pseudo_depth = depth; break; } if (verify_depth >= 0 && pseudo_depth > verify_depth) { /* The error is deeper than we care to scrutinize. Ignore it. */ ok=1; verify_error=X509_V_OK; } else { X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256); BIO_printf(bio_err,"depth=%d %s\n",depth,buf); BIO_printf(bio_err,"verify error:num=%d:%s\n",err, X509_verify_cert_error_string(err)); } } if (!ok) switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,256); BIO_printf(bio_err,"issuer= %s\n",buf); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: BIO_printf(bio_err,"notBefore="); ASN1_UTCTIME_print(bio_err,X509_get_notBefore(ctx->current_cert)); BIO_printf(bio_err,"\n"); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: BIO_printf(bio_err,"notAfter="); ASN1_UTCTIME_print(bio_err,X509_get_notAfter(ctx->current_cert)); BIO_printf(bio_err,"\n"); break; } /*BIO_printf(bio_err,"verify return:%d\n",ok);*/ return(ok); } void apps_ssl_info_callback(s,where,ret) SSL *s; int where; int ret; { char *str; int w; w=where& ~SSL_ST_MASK; if (w & SSL_ST_CONNECT) str="SSL_connect"; else if (w & SSL_ST_ACCEPT) str="SSL_accept"; else str="undefined"; if (where & SSL_CB_LOOP) { BIO_printf(bio_err,"%s:%s\n",str,SSL_state_string_long(s)); } else if (where & SSL_CB_ALERT) { str=(where & SSL_CB_READ)?"read":"write"; BIO_printf(bio_err,"SSL3 alert %s:%s:%s\n", str, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); } else if (where & SSL_CB_EXIT) { if (ret == 0) BIO_printf(bio_err,"%s:failed in %s\n", str,SSL_state_string_long(s)); else if (ret < 0) { BIO_printf(bio_err,"%s:error in %s\n", str,SSL_state_string_long(s)); } } } /********************************************************************/ #if 0 static void print_stuff(bio,s,full) BIO *bio; SSL *s; int full; { X509 *peer; char *p; static char *space=" "; char buf[BUFSIZ]; STACK *sk; SSL_CIPHER *c; X509_NAME *xn; int j,i; if (full) { sk=SSL_get_peer_cert_chain(s); if (sk != NULL) { BIO_printf(bio,"---\nCertificate chain\n"); for (i=0; i 0)) { BIO_printf(bio,"---\nAcceptable client certificate CA names\n"); for (i=0; ihit)?"---\nReused, ":"---\nNew, ")); c=SSL_get_current_cipher(s); if (c) { BIO_printf(bio,"%s, Cipher is %s\n", SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c)); } else { BIO_printf(bio, "no cipher\n"); } SSL_SESSION_print(bio,SSL_get_session(s)); BIO_printf(bio,"---\n"); } #endif /********************************************************************/ static int child_unreaped = 0; static int child_running = 0; static void probe_child(); /********************************************************************/ #define MY_BUFSIZE 4096 #define IO_SUCCESS 1 #define IO_FAILURE 0 int handshake_io_loop(int sockfd/*UNUSED*/, SSL *con, int polarity) { int rval; /*fprintf(stderr, "entering handshake io loop\n");*/ while (SSL_in_init(con)) { char dummy; /*fprintf(stderr, "SSL_handshake ");*/ rval=SSL_write(con,&dummy, 0); switch (SSL_get_error(con,rval)) { case SSL_ERROR_NONE: /* keep going until we're out of the init */ break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: /*I ought to do something about this if I activate NBIO*/ break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out,"want X509 lookup?!\n"); return IO_FAILURE; break; case SSL_ERROR_ZERO_RETURN: /* yeah, whatever */ break; case SSL_ERROR_SYSCALL: if (rval < 0) { BIO_printf(bio_err,"write:errno=%d\n", errno); exit(EXITCODE_SYSCALLFAILED); /* don't even return. Bail immediately */ } break; case SSL_ERROR_SSL: ERR_print_errors(bio_err); return IO_FAILURE; case SSL_ERROR_WANT_CONNECT: /* this shouldn't happen to this program */ break; } } /*fprintf(stderr, "\n");*/ return IO_SUCCESS; } /* */ /* a helper function to be called when the main IO loop decides to shut down the connection */ void mIOl_shutdown(int *my_in_p, int *my_out_p) { if (*my_in_p>=0) { close(*my_in_p); if (*my_out_p == *my_in_p) { *my_out_p = -1; } *my_in_p = -1; } if (*my_out_p>=0) { close(*my_out_p); *my_out_p = -1; } } /* a helper function to be called when the main IO loop encounters a system call error */ void mIOl_syscall_error(int *my_in_p, int *my_out_p) { fprintf(stderr, "%s: error in system call ", progname); perror(""); mIOl_shutdown(my_in_p, my_out_p); } /* a helper function to be called when the main IO loop encounters an SSL library error */ void mIOl_ssl_error(int *my_in_p, int *my_out_p) { ERR_print_errors(bio_err); mIOl_shutdown(my_in_p, my_out_p); } int main_io_loop(int my_in, int sockfd, SSL *con, int my_out, int polarity) { int read_tty, write_tty, tty_on; int read_ssl, write_ssl; int width; int cbuf_len, cbuf_off, sbuf_len, sbuf_off; char cbuf[MY_BUFSIZE], sbuf[MY_BUFSIZE]; fd_set readfds, writefds; int ret = IO_FAILURE; int rval; #ifdef DEBUG fprintf(stderr, "entering main io loop\n"); #endif /* ok, lets connect */ width=sockfd; if (my_in >width) width = my_in; if (my_out>width) width = my_out; width++; read_tty=1; write_tty=0; tty_on=1; read_ssl=1; write_ssl=1; cbuf_len=0; cbuf_off=0; sbuf_len=0; sbuf_off=0; while (my_in>=0 || my_out>=0) { FD_ZERO(&readfds); FD_ZERO(&writefds); if (tty_on) { if (read_tty && my_in>=0) FD_SET(my_in,&readfds); if (write_tty && my_out>=0) FD_SET(my_out,&writefds); } if (read_ssl) FD_SET(SSL_get_fd(con),&readfds); if (write_ssl) FD_SET(SSL_get_fd(con),&writefds); #ifdef DEBUG fprintf(stderr, "select(%ld, %ld, %ld) = ", *(long*)&readfds, *(long*)&writefds, 0L); #endif rval=select(width,&readfds,&writefds,NULL,NULL); #ifdef DEBUG fprintf(stderr, "(%ld, %ld, %ld)\n", *(long*)&readfds, *(long*)&writefds, 0L); #endif if ( rval < 0) { mIOl_syscall_error(&my_in, &my_out); break; } if (FD_ISSET(SSL_get_fd(con),&writefds)) { #ifdef DEBUG fprintf(stderr, "SSL_write "); #endif rval=SSL_write(con,&(cbuf[cbuf_off]), (unsigned int)cbuf_len); #ifdef DEBUG fprintf(stderr, "%d bytes (of %d)\n", rval, cbuf_len); #endif switch (SSL_get_error(con,rval)) { case SSL_ERROR_NONE: if (rval < 0) { mIOl_syscall_error(&my_in, &my_out); break; } cbuf_off+=rval; cbuf_len-=rval; if (cbuf_len <= 0) { read_tty=1; write_ssl=0; } else /* if (cbuf_len > 0) */ { read_tty=0; write_ssl=1; } break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out,"write W BLOCK\n"); write_ssl=1; read_tty=0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out,"write R BLOCK\n"); write_tty=0; read_ssl=1; write_ssl=0; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out,"write X BLOCK\n"); break; case SSL_ERROR_ZERO_RETURN: if (cbuf_len != 0) { BIO_printf(bio_c_out,"shutdown\n"); mIOl_shutdown(&my_in, &my_out); ret = IO_SUCCESS; break; } else { read_tty=1; write_ssl=0; break; } case SSL_ERROR_SYSCALL: if ((rval != 0) || (cbuf_len != 0)) { if (errno == EINTR) { /* big deal. Keep on keepin on */ } else { mIOl_syscall_error(&my_in, &my_out); } } else { read_tty=1; write_ssl=0; } break; case SSL_ERROR_SSL: mIOl_ssl_error(&my_in, &my_out); break; } } #ifndef WINDOWS else if (FD_ISSET(my_out,&writefds)) { rval=write(my_out,&(sbuf[sbuf_off]),sbuf_len); #ifdef DEBUG fprintf(stderr, "wrote %d bytes (of %d)\n", rval, sbuf_len); #endif if (rval < 0) { mIOl_syscall_error(&my_in, &my_out); break; } sbuf_len-=rval;; sbuf_off+=rval; if (sbuf_len <= 0) { read_ssl=1; write_tty=0; } } #endif else if (FD_ISSET(SSL_get_fd(con),&readfds)) { #ifdef DEBUG fprintf(stderr, "SSL_read "); #endif rval=SSL_read(con,sbuf,MY_BUFSIZE); #ifdef DEBUG fprintf(stderr, "%d bytes\n", rval); #endif switch (SSL_get_error(con,rval)) { case SSL_ERROR_NONE: if (rval < 0) { mIOl_syscall_error(&my_in, &my_out); break; } else { sbuf_off=0; sbuf_len=rval; read_ssl=0; write_tty=1; } break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out,"read W BLOCK\n"); write_ssl=1; read_tty=0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out,"read R BLOCK\n"); write_tty=0; read_ssl=1; if ((read_tty == 0) && (write_ssl == 0)) write_ssl=1; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out,"read X BLOCK\n"); break; case SSL_ERROR_SYSCALL: mIOl_syscall_error(&my_in, &my_out); break; case SSL_ERROR_ZERO_RETURN: mIOl_shutdown(&my_in, &my_out); ret = IO_SUCCESS; break; case SSL_ERROR_SSL: mIOl_ssl_error(&my_in, &my_out); break; } } #ifndef WINDOWS else if (FD_ISSET(my_in,&readfds)) { rval=read(my_in,cbuf,MY_BUFSIZE); #ifdef DEBUG fprintf(stderr, "read %d bytes\n", rval); #endif if ( rval < 0 ) { mIOl_syscall_error(&my_in, &my_out); break; } else if (rval ==0) { mIOl_shutdown(&my_in, &my_out); ret = IO_SUCCESS; break; } else { cbuf_len=rval; cbuf_off=0; read_tty=0; write_ssl=1; } } #endif } SSL_shutdown(con); close(SSL_get_fd(con)); return ret; } /********************************************************************/ int socket_ioctl(fd,type,arg) int fd; long type; unsigned long *arg; { int i,err; #ifdef WINDOWS i=ioctlsocket(fd,type,arg); #else i=ioctl(fd,type,arg); #endif if (i < 0) { #ifdef WINDOWS err=WSAGetLastError(); #else err=errno; #endif BIO_printf(bio_err,"ioctl on socket failed:error %d\n",err); } return(i); } /********************************************************************/ static int childpid = -1; static void waitonchild() { /* got a SIGCHILD. It must be that: */ child_running = 0; } int local_return_code=0; static void probe_child() { if (child_running || !child_unreaped) return; if ( 0>=wait(&local_return_code)) { fprintf(stderr, "%s: wait returned error or zero: ", progname); perror(""); exit(EXITCODE_SYSCALLFAILED); } if (!WIFEXITED(local_return_code)) local_return_code = EXITCODE_SIGNAL; else local_return_code = WEXITSTATUS(local_return_code); child_unreaped = 0; } static void spawn_child(sp_infd, sp_outfd, parent_inp, parent_outp, cmd) int sp_infd, sp_outfd; int *parent_inp, *parent_outp; char **cmd; { int tochild[2] = { -1, -1 }; int fromchild[2] = { -1, -1 }; int sockpr[2] = { -1, -1 }; signal(SIGCHLD,waitonchild); child_running = 1; /* well, not yet. */ child_unreaped = 1; if (!valid_descriptor(sp_infd)) dup2(0, sp_infd); /* "reserve" the fd */ if (!valid_descriptor(sp_outfd)) dup2(0, sp_outfd); /* "reserve" the fd */ if (sp_infd>=0 && sp_infd == sp_outfd) { if (0!=socketpair(AF_UNIX, SOCK_STREAM, 0/*let it choose*/, sockpr)) { fprintf(stderr, "%s: totally failed to socketpair(2): ", progname); perror(""); exit (EXITCODE_SYSCALLFAILED); } } else { if (sp_infd>=0) { if (pipe(tochild) !=0) { fprintf(stderr, "%s: totally failed to pipe(2): ", progname); perror(""); exit (EXITCODE_SYSCALLFAILED); } } if (sp_outfd>=0) { if (pipe(fromchild) !=0) { fprintf(stderr, "%s: totally failed to pipe(2): ", progname); perror(""); exit (EXITCODE_SYSCALLFAILED); } } } fflush(stdout); fflush(stderr); childpid = fork(); if (childpid<0) { fprintf(stderr, "%s: unable to fork: ", progname); perror(""); /* I would clear child_running, but, look at the next line */ exit(EXITCODE_SYSCALLFAILED); } /* now there's a child running (assuming no race conditions, which is why I set it up above and not here. I'm stupid, but paranoid). */ if (childpid==0) { /* child */ if (sp_infd>=0 && sp_infd == sp_outfd) { close(sockpr[1]); dup2(sockpr[0], sp_infd); dup2(sockpr[0], sp_outfd); close(sockpr[0]); } else { if (sp_infd>=0) { close(tochild[1]); dup2(tochild[0], sp_infd); close(tochild[0]); } if (sp_outfd>=0) { close(fromchild[0]); dup2(fromchild[1], sp_outfd); close(fromchild[1]); } } execvp(*cmd, cmd); fprintf(stderr, "%s: Unable to exec %s: ", progname, *cmd); perror(""); exit(EXITCODE_EXECFAILED); } else { /* parent */ if (sp_infd>=0 && sp_infd == sp_outfd) { close(sockpr[0]); *parent_outp = *parent_inp = sockpr[1]; } else { if (sp_infd>=0) { *parent_outp = tochild[1]; close(tochild[0]); /* close(1); */ } if (sp_outfd>=0) { *parent_inp = fromchild[0]; close(fromchild[1]); /* close(0); */ } } } } /********************************************************************/ static int scan_flag_numeric_fd(s, fdp) char *s; int *fdp; { int n; if (1 != sscanf(s, "%i%n", fdp, &n)) { fprintf(stderr, "%s: parse error in file descriptor list at '%s'\n", progname, s); exit(EXITCODE_ARGS); } return n; } /********************************************************************/ #ifndef NO_RSA static RSA *tmp_rsa_cb(s,export) SSL *s; int export; { int s_quiet = 0;/* hack, RF */ static RSA *rsa_tmp=NULL; if (rsa_tmp == NULL) { if (!s_quiet) { BIO_printf(bio_err,"Generating temp (512 bit) RSA key..."); BIO_flush(bio_err); } rsa_tmp=RSA_generate_key(512,RSA_F4,NULL); if (!s_quiet) { BIO_printf(bio_err,"\n"); BIO_flush(bio_err); } } return(rsa_tmp); } #endif /********************************************************************/ void fprintf_cipher_list(fp, sk) FILE *fp; STACK *sk; { SSL_CIPHER *ciph; int i,j; j=sk_num(sk); for (i=0; i0) putc(':', fp); fputs(SSL_CIPHER_get_name(ciph), fp); #endif } } void app_set_cipher_list(ctx, cipher, verbose) SSL_CTX *ctx; char *cipher; int verbose; { if (cipher && ! SSL_CTX_set_cipher_list(ctx,cipher)) { fprintf(stderr, "%s: failed to set ciphers to %s\n", progname, cipher); usage(); exit(EXITCODE_ARGS); } if (verbose) { SSL *temp; temp = SSL_new(ctx); if (temp ==0) { fprintf(stderr, "%s: failed to allocate temporary SSL*", progname); exit(1); } fprintf(stderr, "%s: active ciphers: ", progname); fprintf_cipher_list(stderr, SSL_get_ciphers(temp)); fprintf(stderr, "\n"); SSL_free(temp); } } static int sockfd = -1; static int infd= -1, outfd= -1; static SSL_CTX *ctx=0; int main (argc,argv) int argc; char ** argv; { char *cert_file=0; char *key_file=0; char *CAfile = 0, *CApath=0; int verify = SSL_VERIFY_NONE; char *cipher = 0; int connect_not_accept = -1; int c_quiet = 0; int state = 0; int c_nbio = 0; int bugs=0; struct ssl_criteria_node *criteria = 0; SSL *con =0; SSL_METHOD *meth=NULL; BIO *sbio; /*fprintf(stderr, "%s starting\n", argv[0]);*/ set_progname(argv[0]); /*apps_startup()*/ signal(SIGPIPE, SIG_IGN); SSLeay_add_all_algorithms(); #if !defined(NO_SSL2) && !defined(NO_SSL3) meth=SSLv23_method(); #elif !defined(NO_SSL3) meth=SSLv3_method(); #elif !defined(NO_SSL2) meth=SSLv2_method(); #endif while (argc>1) { char *arg = argv[1]; if (arg[0] != '-') { break; } arg++; if (arg[0] == '-') { arg++; if (0==strcmp(arg, "verbose")) { verbose = 1; argv++; argc--; } else if (0==strcmp(arg, "fd")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --fd requires file number for socket.\n", progname); usage(); exit(EXITCODE_ARGS); } else { sockfd = atoi(argv[1]); argv++; argc--; } } else if (0==strcmp(arg, "verify")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --verify requires depth.\n", progname); usage(); exit(EXITCODE_ARGS); } else { verify = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE; verify_depth = atoi(argv[1]); argv++; argc--; } } else if (0==strcmp(arg, "cert")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --cert requires path to certificate.\n", progname); usage(); exit(EXITCODE_ARGS); } else { cert_file = argv[1]; argv++; argc--; } } else if (0==strcmp(arg, "key")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --key requires path to certificate.\n", progname); usage(); exit(EXITCODE_ARGS); } else { key_file = argv[1]; argv++; argc--; } } else if (0==strcmp(arg, "CApath")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --CApath requires path to certificate directory.\n", progname); usage(); exit(EXITCODE_ARGS); } else { CApath = argv[1]; argv++; argc--; } } else if (0==strcmp(arg, "CAfile")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --CAfile requires path to certificate file.\n", progname); usage(); exit(EXITCODE_ARGS); } else { CAfile = argv[1]; argv++; argc--; } } else if (0==strcmp(arg, "cipher")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --cipher needs cipher name.\n", progname); usage(); exit(EXITCODE_ARGS); } else { cipher = argv[1]; argv++; argc--; } #ifdef FIONBIO } else if (strcmp(*argv,"nbio") == 0) { c_nbio=1; #endif } else if (0==strcmp(arg, "server")) { connect_not_accept = 0; argv++; argc--; } else if (0==strcmp(arg, "client")) { connect_not_accept = 1; argv++; argc--; } else if (0==strcmp(arg, "criteria")) { int d_argc; argv++; argc--; criteria = ssl_criteria_argv_rdp_unary(argv+1, argc-1, &d_argc); if (criteria == 0) { fprintf(stderr, "%s: Bad criteria\n", progname); usage(); exit(EXITCODE_ARGS); } argv+= d_argc; argc-= d_argc; } else if (0==strcmp(arg, "subproc")) { subproc = 1; argv++; argc--; } else if (0==strcmp(arg, "infd")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --%s requires descriptor number.\n", progname, arg); usage(); exit(EXITCODE_ARGS); } else { int offset = scan_flag_numeric_fd(argv[1], &infd); if (argv[1][offset] != 0) { fprintf(stderr, "%s: bad descriptor number %s.\n", progname, argv[1]); usage(); exit(EXITCODE_ARGS); } argv++; argc--; } } else if (0==strcmp(arg, "outfd")) { argv++; argc--; if (argc<2) { fprintf(stderr, "%s: --%s requires descriptor number.\n", progname, arg); usage(); exit(EXITCODE_ARGS); } else { int offset = scan_flag_numeric_fd(argv[1], &outfd); if (argv[1][offset] != 0) { fprintf(stderr, "%s: bad descriptor number %s.\n", progname, argv[1]); usage(); exit(EXITCODE_ARGS); } argv++; argc--; } } else { /* unknown -- flag. Assume it's a command :) */ break; } } else { /* it's a set of single dash flags. */ do { switch (arg[0]) { case '#': arg += scan_flag_numeric_fd(arg+1, &sockfd); break; case 'v': verbose = 1; break; case 's': subproc=1; break; case 'i': arg += scan_flag_numeric_fd(arg+1, &infd); break; case 'o': arg += scan_flag_numeric_fd(arg+1, &outfd); break; case 0: fprintf(stderr, "%s: blank compact flag.\n", progname); /* fall through */ default: fprintf(stderr, "%s: Unknown compact flag beginning %s\n", progname, arg); usage(); exit (EXITCODE_ARGS); } arg++; } while (arg[0]); argv++; argc--; } } /*fprintf(stderr, "arguments are parsed\n");*/ /********************/ /* verify arguments */ /********************/ /* argv+1 points to an unrecognized flag that must be the subprocess cmd and arguments. */ if (argc>1 && !subproc) { fprintf(stderr, "%s: Unknown flag %s\n", progname, argv[1]); usage(); exit (EXITCODE_ARGS); } if (argc <=1 && subproc) { fprintf(stderr, "%s: no subprocess specified %s\n", progname, argv[1]); usage(); exit (EXITCODE_ARGS); } if (sockfd<0) { fprintf(stderr, "%s: I must know the file number for the socket.\n", progname); usage(); exit(EXITCODE_ARGS); } if (connect_not_accept<0) { fprintf(stderr, "%s: You must specify either --server or --client.\n", progname); usage(); exit(EXITCODE_ARGS); } if (subproc) { /* check to make sure at least one descriptor is rerouted */ if (infd<0 && outfd<0) { fprintf(stderr, "%s: must redirect at least one descriptor of subprocess. (--infd or --outfd)\n", progname); usage(); exit(EXITCODE_ARGS); } } if (verbose) emit_version("ssl-auth", 1996); /********************/ /* do ssl stuff */ /********************/ if (bio_err == NULL) { if (c_quiet) { bio_err=BIO_new(BIO_s_null()); } else { if (bio_err == NULL) bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); } } #if 0 if (bio_c_out == NULL) { if (c_quiet) { bio_c_out=BIO_new(BIO_s_null()); } else { if (bio_c_out == NULL) bio_c_out=BIO_new_fp(stdout,BIO_NOCLOSE); } } #else bio_c_out = bio_err; #endif SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); ctx=SSL_CTX_new(meth); if (ctx == NULL) { ERR_print_errors(bio_err); exit(EXITCODE_SSL_ERROR); /*goto end;*/ } if (bugs) SSL_CTX_set_options(ctx,SSL_OP_ALL); if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback); #ifndef NO_RSA SSL_CTX_set_tmp_rsa_callback(ctx,tmp_rsa_cb); #endif /*SSL_CTX_set_cipher_list(ctx,cipher);*/ app_set_cipher_list(ctx, cipher, verbose); if ( criteria != 0 ) { debug_print_criteria_node(stderr, criteria, 0); SSL_CTX_set_cert_verify_cb(ctx,replacement_X509_verify_cert,(char*)criteria); verify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_CLIENT_ONCE; } SSL_CTX_set_verify(ctx,verify,verify_callback); /* SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(s_cert_file));*/ if (!set_cert_stuff(ctx,cert_file,key_file)) { exit (EXITCODE_SSL_ERROR); /*goto end;*/ } if ((!SSL_CTX_load_verify_locations(ctx,CAfile,CApath)) || (!SSL_CTX_set_default_verify_paths(ctx))) { BIO_printf(bio_err,"error setting default verify locations\n"); ERR_print_errors(bio_err); exit (EXITCODE_SSL_ERROR); /*goto end;*/ } con=(SSL *)SSL_new(ctx); #ifdef FIONBIO c_nbio = 0; /* I do not have code in the handshake to handle this yet */ if (c_nbio) { unsigned long l=1; BIO_printf(bio_c_out,"turning on non blocking io\n"); socket_ioctl(sockfd,FIONBIO,&l); } #endif sbio=BIO_new_socket(sockfd,BIO_NOCLOSE); SSL_set_bio(con,sbio,sbio); if (connect_not_accept) SSL_set_connect_state(con); else SSL_set_accept_state(con); { int my_in = 0; int my_out = 1; int ok; ok = handshake_io_loop(sockfd, con, connect_not_accept); if (ok) { /*print_stuff(bio_c_out,con,1);*/ if (subproc) { /* XXX - yeah, I should add options for stdin/out pass-through */ my_in = -1; my_out = -1; spawn_child(infd, outfd, &my_in, &my_out, argv+1); } main_io_loop(my_in, sockfd, con, my_out, connect_not_accept); } else { /* handshake failed */ exit (EXITCODE_SSL_ERROR); } } if (con != NULL) SSL_free(con); if (ctx != NULL) SSL_CTX_free(ctx); if (bio_c_out != NULL && bio_c_out != bio_err) { BIO_free(bio_c_out); bio_c_out=NULL; } if (bio_err != NULL) { BIO_free(bio_err); bio_err=NULL; } while (child_running) { pause(); } probe_child(); exit(local_return_code); }