/* $Id: main.c,v 1.20 2004/03/14 13:15:31 thivillon Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../config.h" #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_PTY_H #include #endif #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_SYS_FILE_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_SYS_FILIO_H #include #endif #ifdef HAVE_UTIL_H #include #endif #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif #ifndef HAVE_OPENPTY extern int openpty(int *, int *, char *, struct termios *, struct winsize *); #endif #ifdef WITH_LIBWRAP #include #define TCPD_DAEMON_NAME "ssltunnel" #endif #include "ssltun.h" #include "session.h" extern void initsetproctitle(int argc, char **argv, char **envp); #ifndef HAVE_SETPROCTITLE extern void setproctitle(char *fmt, ...); #endif extern DH *ssl_callback_TmpDH(SSL *s, int is_export, int keylength); int init_socket( int port ); void sig_chld_master (int status); void sig_chld_slave (int status); void sig_alrm_slave (int status); void sig_int_slave (int status); void sig_int_master (int status); SSL *tcp_accept_fork ( ); int do_server_loop ( SSL *ssl ); int berr_exit(char *string); SSL_CTX *initialize_ctx(char *calist, char *certfile, char *keyfile); void destroy_ctx(SSL_CTX *ctx); RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength); void sockerror(char *string) ; char *clientname; ClientData *getclientdata(char *subject); key_value_t *file_parse ( char *line , bool *err); int read_config_file (char *filename); static void x509_fingerprint (char *s, int l, X509 * cert); void kill_then_wait_childs(); static int ssl_accept_timeout(SSL *ssl, int tmo); static int ssl_write_timeout(SSL *ssl, char *buffer, int size, int tmo); static int ssl_read_timeout(SSL *ssl, char *buffer, int size, int tmo); #ifndef HAVE_OPENPTY int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp); #endif int socklisten[16]; int nbchild=0; pid_t *child_ids; int verbose=1; unsigned long inbytes=0; unsigned long outbytes=0; BIO *bio_err=0; SSL_CTX *ctx; pid_t pppid = -1; extern int wtmpfd; int stopped = 0; #ifdef WITH_LIBWRAP /* We need these definitions, since libwrap declares these as extern */ int allow_severity = LOG_DAEMON; int deny_severity = LOG_WARNING; #endif tunnel *cfgtunnel; int main(int argc, char **argv, char **envp) { SSL *ssl; char configfilename[256]; char pidstring[32]; int dev_null; int index; int fpid; struct stat lockdirs; if (argc > 2) { fprintf(stderr,"%s [configfile]\n",argv[0]); exit(2); } cfgtunnel = malloc (sizeof (tunnel)); cfgtunnel->keyfile = KEYFILE; cfgtunnel->certfile = CERTFILE; cfgtunnel->cacertfile = CAFILE; cfgtunnel->userfile = USERFILE; cfgtunnel->maxclients = MAXCHILD; cfgtunnel->network_timeout = DEFAULT_NETWORK_TIMEOUT; cfgtunnel->listenaddr = NULL; cfgtunnel->wtmp = WTMPFILE ; cfgtunnel->pidfile = DEFAULT_PIDFILE; cfgtunnel->port = DEFAULT_PORT; cfgtunnel->lockdir = DEFAULT_LOCKDIR; if (argc == 2 ) { strncpy(configfilename , argv[1], 255); } else { strncpy(configfilename , DEFAULT_CONFIG_FILENAME, 255); } if ( read_config_file( configfilename ) ) { exit ( 1 ); } openlog ( "pppserver" , LOG_PID, SYSLOG_FAC); syslog (LOG_NOTICE, "pppserver version %s using %s", VERSION, OPENSSL_VERSION_TEXT); #ifdef HAVE_DAEMON if (daemon(0,1) < 0) { perror("daemon"); exit(1); } #else switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); (void)chdir("/"); #endif initsetproctitle(argc, argv, envp); setproctitle("initialising"); close (0); close (1); close (2); dev_null = open("/dev/null", O_RDWR); dup2 ( dev_null, 0); dup2 ( dev_null, 1); dup2 ( dev_null, 2); if (dev_null > 2) close (dev_null); ctx = initialize_ctx(cfgtunnel->cacertfile, cfgtunnel->certfile, cfgtunnel->keyfile); child_ids = malloc ( cfgtunnel->maxclients * sizeof (pid_t)); for ( index = 0; index < cfgtunnel->maxclients; index++) *( child_ids + index) = -1; signal ( SIGCHLD, &sig_chld_master ) ; signal ( SIGINT, &sig_int_master ) ; signal ( SIGTERM, &sig_int_master ) ; init_socket (cfgtunnel->port); /* Create pid file */ unlink(cfgtunnel->pidfile); if ((fpid = open(cfgtunnel->pidfile, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH )) < 0) { syslog(LOG_ERR, "Unable to open %s: %s", cfgtunnel->pidfile, strerror(errno)); exit (1); } snprintf(pidstring, 31 , "%d\n", (int) getpid()); write(fpid, pidstring, strlen(pidstring)); close(fpid); /* Create/check lockdir */ mkdir(cfgtunnel->lockdir, S_IRWXU); chmod(cfgtunnel->lockdir, S_IRWXU); if ( lstat(cfgtunnel->lockdir, &lockdirs) || (lockdirs.st_mode & S_IFMT) != S_IFDIR || (lockdirs.st_uid != getuid()) || (lockdirs.st_mode & S_IRWXG) != 0 || (lockdirs.st_mode & S_IRWXO) != 0) { syslog(LOG_ERR, "Unable to create %s securely", cfgtunnel->lockdir); } /* Clean sessions */ clean_sessions(cfgtunnel->wtmp); while (!stopped) { if ( (ssl = tcp_accept_fork ( )) != NULL) { do_server_loop ( ssl ); update_wtmp(cfgtunnel->wtmp, "", getpid(), ""); exit(0); } } /* Kill and wait child */ kill_then_wait_childs(); syslog ( LOG_NOTICE, "pppserver exiting"); unlink(cfgtunnel->pidfile); return 0; } int init_socket( int port ) { int sock= -1 ; struct sockaddr_in sin; int one = 1; char *addrs; char *uniqueaddr; int index; index = 0; if (cfgtunnel->listenaddr != NULL) { /* Bind all addresses given */ addrs = strdup ( cfgtunnel->listenaddr ); for ( uniqueaddr = strtok (addrs, " ,;"); uniqueaddr && index < 15; uniqueaddr = strtok ( NULL, " ,;")) { if ( ( sock = socket (AF_INET , SOCK_STREAM, 0 ) ) < 0 ) { syslog(LOG_ERR, "socket: %s", strerror(errno)); exit (-1) ; } setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); syslog ( LOG_INFO, "binding to %s", uniqueaddr ); memset( &sin, sizeof(struct sockaddr_in), 0 ); sin.sin_family = AF_INET; sin.sin_port = ntohs ( port ); sin.sin_addr.s_addr = inet_addr ( uniqueaddr ); if ( ( bind ( sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in) ))) { syslog(LOG_ERR, "bind: %s", strerror(errno)); exit (-1); } if (listen (sock, 6)) { syslog(LOG_ERR, "listen: %s", strerror(errno)); exit (-1); } if(ioctl(sock, FIONBIO, &one)<0) { syslog(LOG_ERR, "ioctl: %s", strerror(errno)); exit (-1); } socklisten[index++] = sock; } free ( addrs ); } else { if ( ( sock = socket (AF_INET , SOCK_STREAM, 0 ) ) < 0 ) { syslog(LOG_ERR, "socket: %s", strerror(errno)); exit (-1) ; } /* Bind INET_ANY */ memset( &sin, sizeof(struct sockaddr_in), 0 ); sin.sin_family = AF_INET; sin.sin_port = ntohs ( port ); sin.sin_addr.s_addr = INADDR_ANY; setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); if ( ( bind ( sock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in) ))) { syslog(LOG_ERR, "bind: %s", strerror(errno)); exit (-1); } if (listen (sock, 6)) { syslog(LOG_ERR, "listen: %s", strerror(errno)); exit (-1); } if(ioctl(sock, FIONBIO, &one)<0) { syslog(LOG_ERR, "ioctl: %s", strerror(errno)); exit (-1); } socklisten[index++] = sock; } socklisten[index]=-1; return sock; } void sig_chld_master (int status) { pid_t child_pid; int index; while ((child_pid=waitpid(-1, NULL, WNOHANG)) > 0) { nbchild--; for (index = 0; index < cfgtunnel->maxclients; index ++) { if (child_pid == *(child_ids + index)) { *(child_ids + index) = -1; break; } } } signal ( SIGCHLD, &sig_chld_master ) ; } void sig_chld_slave (int status) { pid_t child_pid; while ((child_pid = waitpid(-1, NULL, WNOHANG)) > 0) { nbchild --; if (child_pid == pppid) { pppid = -1; stopped = 1; } } signal ( SIGCHLD, &sig_chld_slave ) ; } SSL *tcp_accept_fork ( ) { int sock=-1; int newsock; struct sockaddr_in peeraddr; unsigned int len; int pid; X509 *xs; char *cp; SSL *ssl; int r,num; ClientData *myClient=NULL; int valid_cert; int tuyauIn[2]; int tuyauOut[2]; int masterfd,slavefd; struct termios blah; char buffer[SIZE]; char fingerprint[256]; fd_set rset; int index; int maxsocket; int nd; int one=1; char *ptr; char prot[PROT_SIZE]; char client_version[PROT_SIZE]; char fingerlockfile[1024]; struct flock locks; int fdlock; char *ptrsrc,*ptrdest; #ifdef WITH_LIBWRAP struct request_info request; #endif FD_ZERO(&rset); maxsocket = 0; for ( index=0;index < 15 && socklisten[index] >=0; index++ ) { FD_SET(socklisten[index], &rset); if ( socklisten[index] > maxsocket ) maxsocket = socklisten[index]; } setproctitle("accepting connections"); while ( (nd = select ( maxsocket + 1, &rset, NULL, NULL, NULL )) < 0 && errno == EINTR && !stopped) ; if (nd <=0 ) { syslog(LOG_INFO, "select: %s", strerror(errno)); return NULL; } for ( index=0;index < 15 && socklisten[index] >=0 ; index++ ) { if (FD_ISSET( socklisten[index], &rset )) { sock = socklisten[index]; break; } } len = sizeof (struct sockaddr_in); newsock = accept( sock , (struct sockaddr *) &peeraddr, &len); if (newsock < 0 ) { syslog(LOG_ERR, "accept: %s", strerror(errno)); return 0; } if (nbchild >= cfgtunnel->maxclients ) { syslog(LOG_ERR, "too many childs !"); close( newsock ); return NULL; } #ifdef WITH_LIBWRAP request_init(&request, RQ_DAEMON, TCPD_DAEMON_NAME , RQ_FILE, newsock , NULL); fromhost(&request); if (!hosts_access(&request)) { syslog(LOG_WARNING,"Refused connection from %s\n", eval_client(&request)); close(newsock); return NULL; } #endif if (newsock > 0) { pid = fork(); if (pid > 0) { close(newsock); for (index = 0; index < cfgtunnel->maxclients; index ++) { if (*(child_ids + index) == - 1) { *(child_ids + index) = pid; break; } } nbchild++; return NULL; } else if (pid == 0 ) { for ( index=0;index < 15 && socklisten[index] >=0 ; index++ ) { close(socklisten[index]); } stopped = 0; signal ( SIGCHLD, &sig_chld_slave ) ; signal(SIGINT, &sig_int_slave); signal(SIGALRM, &sig_alrm_slave); signal(SIGTERM, &sig_int_slave); setproctitle("accepting connection from %s", inet_ntoa(peeraddr.sin_addr)); syslog(LOG_NOTICE, "Accepting connection from %s", inet_ntoa(peeraddr.sin_addr)); ssl=SSL_new(ctx); if (ssl == NULL) { close(newsock); berr_exit("SSL_new"); } /* Set non block */ if(ioctl(newsock, FIONBIO, &one)<0) { syslog(LOG_ERR, "ioctl: %s", strerror(errno)); exit (-1); } setsockopt( newsock, 6, TCP_NODELAY, &one, sizeof(int)); SSL_set_fd(ssl,newsock); SSL_set_accept_state(ssl); setproctitle("SSL starting with %s", inet_ntoa(peeraddr.sin_addr)); r= ssl_accept_timeout(ssl, cfgtunnel->network_timeout); if (r <= 0) { close(newsock); exit(0); } /* Check Cert */ valid_cert = 0; xs = NULL; if ((xs = SSL_get_peer_certificate(ssl)) == NULL) { syslog(LOG_NOTICE, "No valid cert !"); goto catch; } /* Check subject, look into database */ cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); if (cp!=NULL) { syslog(LOG_NOTICE, "Client certificate : %s\n", cp); myClient = getclientdata ( cp ); valid_cert = ( myClient != NULL && myClient->exec != NULL) ; free(cp); } if (!valid_cert) { syslog(LOG_NOTICE, "No valid data for this user"); if (xs!=NULL) X509_free(xs); goto catch; } /* Check fingerprint */ memset(fingerprint, 0, 256); x509_fingerprint (fingerprint, 255, xs); syslog(LOG_INFO, "Certificate fingerprint: %s", fingerprint); if (myClient && myClient->fingerprint && strcmp(fingerprint,myClient->fingerprint)) { syslog(LOG_NOTICE, "Invalid fingerprint"); X509_free(xs); goto catch; } /* Check issuer */ cp = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0); if (cp!=NULL) { syslog(LOG_INFO, "Issuer : %s", cp); if (( myClient->issuer != NULL) && strcmp(cp, myClient->issuer)) { syslog(LOG_NOTICE, "Invalid issuer"); free(cp); X509_free(xs); goto catch; } free(cp); } else { syslog(LOG_NOTICE, "No Issuer !"); X509_free(xs); goto catch; } X509_free(xs); /* Create lockfile with cert fingerprint */ memset(&locks,0,sizeof(struct flock)); locks.l_type = F_WRLCK; locks.l_whence = SEEK_SET; strcpy(fingerlockfile, cfgtunnel->lockdir); strcat(fingerlockfile,"/"); ptrdest = fingerlockfile + strlen(fingerlockfile); ptrsrc = fingerprint; while (*ptrsrc && ptrdest < (fingerlockfile + 1022)) { if (*ptrsrc != ':' && *ptrsrc != '/') { *ptrdest = *ptrsrc; ptrdest++; } ptrsrc++; } *ptrdest = '\0'; if (((fdlock = open(fingerlockfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) < 0 ) || (fcntl(fdlock, F_SETLK, &locks) == -1) ) { syslog(LOG_NOTICE, "Unable to lock %s", fingerlockfile); goto catch; } update_wtmp(cfgtunnel->wtmp, myClient->subject, getpid(), inet_ntoa(peeraddr.sin_addr) ); if (myClient->gid) { syslog(LOG_INFO, "setgid to %d", myClient->gid); if (setgid(myClient->gid)) { syslog(LOG_ERR, "Unable to setgid %d : %s", myClient->gid, strerror(errno)); goto catch; } if (setgroups( 1, &myClient->gid)) { syslog(LOG_ERR, "Unable to setgroups %d : %s", myClient->gid, strerror(errno)); goto catch; } } if (myClient->uid) { syslog(LOG_INFO, "setuid to %d", myClient->uid); if (setuid(myClient->uid)) { syslog(LOG_ERR, "Unable to setuid %d : %s", myClient->uid, strerror(errno)); goto catch; } } SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); /* Send and Receive Banner */ sprintf(buffer, SERVER_BANNER, VERSION); num=ssl_write_timeout(ssl, buffer, strlen(buffer), cfgtunnel->network_timeout ); if (num != strlen(buffer)) { syslog(LOG_INFO, "Error writing banner"); goto catch; } memset(buffer,0,SIZE); num=ssl_read_timeout(ssl, buffer, SIZE-1, cfgtunnel->network_timeout ); if (num <= 0) { syslog(LOG_INFO, "Error reading banner"); goto catch; } if ((ptr=strstr(buffer,"SSL-TUNNEL/")) != NULL) { num = 0; ptr += 11; while (*ptr && *ptr != ' ' && *ptr != '\r' && *ptr != '\n' && num < PROT_SIZE -1) { client_version[num]= *ptr; num++; ptr ++; } client_version[num] = 0; syslog(LOG_INFO, "Client version : %s", client_version); } if ((ptr=strstr(buffer,"PROT/"))!=NULL) { num = 0; ptr += 5; while (*ptr && *ptr != '\r' && *ptr != '\n' && num < PROT_SIZE -1) { prot[num]= *ptr; num++; ptr ++; } prot[num] = 0; syslog(LOG_INFO, "Client Protocol version : %s", prot); } else { syslog(LOG_ERR, "No valid banner"); goto catch; } if (myClient->pty) { memset(&blah,0,sizeof(blah)); blah.c_cc[VMIN]=1; blah.c_cc[VTIME]=0; blah.c_cflag |= B9600; if (openpty(&masterfd,&slavefd,NULL,&blah,NULL)) { syslog(LOG_ERR, "openpty: %s", strerror(errno)); goto catch; }; tuyauIn[0]= slavefd; tuyauOut[0]= masterfd; tuyauIn[1]= masterfd; tuyauOut[1]= slavefd; } else { if (pipe((int *) &tuyauOut) != 0) goto catch; if (pipe((int *) &tuyauIn) != 0) goto catch; } nbchild = 0; if ((pppid = fork()) !=0 ) { nbchild++; close(0); close(1); dup2(tuyauOut[0],0); dup2(tuyauIn[1],1); return ssl; } else if (pppid == 0) { if (wtmpfd != -1) close(wtmpfd); close(fdlock); close(0); close(1); close(newsock); dup2(tuyauIn[0],0); dup2(tuyauOut[1],1); syslog (LOG_INFO, "Exec: %s", myClient->exec ); if (execv ( myClient->exec, myClient->args )) { syslog(LOG_ERR, "execv: %s", strerror(errno)); exit(1); } } else { syslog (LOG_ERR, "fork: %s", strerror(errno)); } catch: update_wtmp(cfgtunnel->wtmp, "", getpid(), ""); SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_free(ssl); ERR_remove_state(0); close(newsock); exit ( 0 ); } else { syslog(LOG_ERR, "fork: %s", strerror(errno)); close(newsock); return NULL; } } return NULL; } int berr_exit(char *string) { syslog(LOG_ERR, "berr_exit: %s", string); exit(0); } SSL_CTX *initialize_ctx(char *calist, char *certfile, char *keyfile) { SSL_METHOD *meth; SSL_CTX *ctx; if(!bio_err){ /* Global system initialization*/ SSL_library_init(); SSL_load_error_strings(); /* An error write context */ bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); } /* Create our context*/ meth=TLSv1_method(); SSL_COMP_add_compression_method(0x42, COMP_rle()); ctx=SSL_CTX_new(meth); /* Load our keys and certificates*/ if(!(SSL_CTX_use_certificate_chain_file(ctx, certfile))) berr_exit("Can't read certificate file"); if(!(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))) berr_exit("Can't read key file"); /* Load the CAs we trust*/ if(!(SSL_CTX_load_verify_locations(ctx, calist ,0))) berr_exit("Can't read CA list"); SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); SSL_CTX_set_tmp_rsa_callback(ctx, &tmp_rsa_callback); /* Prepare for PFS */ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH); return ctx; } void destroy_ctx(SSL_CTX *ctx) { SSL_CTX_free(ctx); } RSA *tmp_rsa_callback(SSL *s, int is_export, int keylength) { RSA *rsa_tmp=NULL; rsa_tmp = RSA_generate_key(keylength,RSA_F4,NULL,NULL); return(rsa_tmp); } int do_server_loop ( SSL *ssl ) { fd_set rd_set, wr_set; int num, fdno, ssl_fd, ssl_bytes, sock_bytes, retval; char sock_buff[2 * SIZE], ssl_buff[2 * SIZE]; int sock_ptr, ssl_ptr, sock_open, ssl_open; unsigned long l; int try_kill; struct timeval tmo; ssl_fd=SSL_get_fd(ssl); fdno=ssl_fd+1; sock_ptr=0; ssl_ptr=0; sock_open=1; ssl_open=1; sock_bytes=0; ssl_bytes=0; l=1; /* ON */ if(ioctl(0, FIONBIO, &l)<0) sockerror("ioctlsocket (sock)"); if(ioctl(1, FIONBIO, &l)<0) sockerror("ioctlsocket (sock)"); if(ioctl(ssl_fd, FIONBIO, &l)<0) sockerror("ioctlsocket (ssl)"); while((sock_open||sock_ptr) && (ssl_open||ssl_ptr) && !stopped) { FD_ZERO(&rd_set); if(sock_open && sock_ptr 0) { sock_ptr += num; outbytes+=num; } else { /* close */ syslog(LOG_INFO, "stdin closed on read"); sock_open = 0; } } if(ssl_open && FD_ISSET(ssl_fd, &rd_set)) { num=SSL_read(ssl, ssl_buff+ssl_ptr, SIZE-ssl_ptr); switch(SSL_get_error(ssl, num)) { case SSL_ERROR_NONE: ssl_ptr+=num; break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: break; case SSL_ERROR_SYSCALL: if(num && errno != EINTR) { /* not EOF */ sockerror("SSL_read (socket)"); goto error; } case SSL_ERROR_ZERO_RETURN: syslog(LOG_INFO, "ssl closed on read"); ssl_open=0; break; case SSL_ERROR_SSL: syslog(LOG_INFO, "ssl error on read"); goto error; } } if(sock_open && FD_ISSET(1, &wr_set)) { num=write(1, ssl_buff, ssl_ptr); if(num<0) { sockerror("write"); goto error; } if(num) { memcpy(ssl_buff, ssl_buff+num, ssl_ptr-num); ssl_ptr-=num; sock_bytes+=num; inbytes+=num; } else { /* close */ syslog(LOG_INFO, "socket closed on write"); sock_open=0; } } if(ssl_open && FD_ISSET(ssl_fd, &wr_set)) { num=SSL_write(ssl, sock_buff, sock_ptr); switch(SSL_get_error(ssl, num)) { case SSL_ERROR_NONE: memcpy(sock_buff, sock_buff+num, sock_ptr-num); sock_ptr-=num; ssl_bytes+=num; break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: break; case SSL_ERROR_SYSCALL: if(num) { /* not EOF */ sockerror("SSL_write (socket)"); goto error; } case SSL_ERROR_ZERO_RETURN: syslog(LOG_INFO, "SSL closed on write"); ssl_open=0; break; case SSL_ERROR_SSL: syslog(LOG_INFO, "SSL error on write"); goto error; } } } retval=0; syslog(LOG_NOTICE,"terminating normally"); retval = 0; goto done; error: syslog(LOG_NOTICE,"terminating abnormally"); retval=-1; done: SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_free(ssl); ERR_remove_state(0); close(ssl_fd); close(0); close(1); syslog(LOG_NOTICE,"In Bytes: %ld, Out Bytes: %ld", inbytes, outbytes); setproctitle("terminating"); try_kill = 0; while (pppid != -1) { syslog(LOG_NOTICE,"killing child"); kill ( pppid, try_kill > 3 ? SIGKILL : SIGTERM ); try_kill ++; tmo.tv_sec = 10; tmo.tv_usec = 0; select (0, NULL, NULL, NULL, &tmo); } syslog(LOG_NOTICE,"Child has exited"); return retval; } void sockerror(char *string) { syslog(LOG_WARNING, "sock error %s", string); } ClientData *getclientdata (char *subject) { FILE *userfile; char line[256]; bool err=false; ClientData *userdata=NULL; char *ptrblank; int index; userfile = fopen (cfgtunnel->userfile, "r"); if (userfile == NULL) { syslog(LOG_ERR, "Unable to open user file: %s", strerror(errno)); return NULL; } while (!err && (fgets( line, 255, userfile) != NULL)) { key_value_t *key_value ; key_value = file_parse ( line , &err) ; if (err == true) { fclose ( userfile ) ; syslog(LOG_ERR, "Unable to parse user file"); return 0 ; } else if (key_value == NULL) { /* white or commenet line : skip it */ continue; } if (!strcasecmp ( key_value->key, "user" )) { if ( !strcasecmp(key_value->value, subject) ) { /* Good user */ userdata = malloc (sizeof(ClientData)); userdata->subject = strdup ( subject ); userdata->exec = NULL; userdata->fingerprint = NULL; userdata->issuer = NULL; userdata->args[0] = NULL; userdata->pty = 0; userdata->uid = 0; userdata->gid = 0; } else if (userdata != NULL) break; } if (!strcasecmp ( key_value->key, "command") && userdata != NULL) { userdata->exec = strdup(key_value->value); userdata->args[0] = strdup(key_value->value); userdata->args[1] = NULL; } if (!strcasecmp ( key_value->key, "pty") && userdata != NULL) { userdata->pty = atoi(key_value->value); } if (!strcasecmp ( key_value->key, "args") && userdata != NULL) { index = 0; while (index < MAXARGS -1 && userdata->args[index] != NULL) index++; for (ptrblank = strtok(key_value->value," \t"); ptrblank; ptrblank = strtok(NULL," \t")) { userdata->args[index++] = strdup(ptrblank); userdata->args[index] = NULL; } } if ( ! strcasecmp( key_value->key , "uid") && userdata != NULL) { userdata->uid = (uid_t) atoi(key_value->value); } if ( ! strcasecmp( key_value->key , "gid") && userdata != NULL) { userdata->gid = (gid_t) atoi(key_value->value); } if (!strcasecmp ( key_value->key, "fingerprint") && userdata != NULL) { userdata->fingerprint = strdup(key_value->value); } if (!strcasecmp ( key_value->key, "issuer") && userdata != NULL) { userdata->issuer = strdup(key_value->value); } } fclose ( userfile ) ; return userdata; } key_value_t *file_parse ( char *line , bool *err) { char *tmp , *key ; key_value_t *key_value ; *err = false; key_value = ( key_value_t * ) malloc ( sizeof ( key_value_t ) ) ; if ( key_value == NULL ) { *err = true; return NULL ; } /* strip comment or final \n */ for ( tmp = line ; *tmp != '\0' ; tmp ++ ) { if ( *tmp == '#' || *tmp == '\n' ) { *tmp = '\0' ; break ; } } /* strip trailing whitespaces */ while ( tmp != line ) { tmp -- ; if ( *tmp == ' ' || *tmp == '\t' ) { *tmp = '\0' ; } else { break ; } } /* find the key */ tmp = line ; while ( *tmp == ' ' || *tmp == '\t' ) { tmp ++ ; } if ( *tmp == '\0' ) /* premature end of line */ { return NULL ; } /* strip the key */ key = tmp ; while ( *tmp != ' ' && *tmp != '\t' && *tmp != '\0' ) { tmp ++ ; } if ( *tmp == '\0' ) /* no value */ { key_value->key = strdup ( key ) ; if ( key_value->key == NULL ) { *err = true; return NULL ; } key_value->value = NULL ; return key_value ; } *tmp = '\0' ; /* copy the key */ key_value->key = strdup ( key ) ; if ( key_value->key == NULL ) { *err = true; return NULL ; } /* strip the blanks */ tmp ++ ; while ( *tmp == ' ' || *tmp == '\t' ) { tmp ++ ; } if ( *tmp == '\0' ) /* premature end of line */ { key_value->value = NULL ; return key_value ; } /* copy the value */ key_value->value = strdup ( tmp ) ; if ( key_value->value == NULL ) { *err = true; return NULL ; } return key_value ; } int read_config_file (char *filename) { FILE *fconfig; bool err=0; char line[256]; fconfig = fopen (filename, "r"); if (fconfig == NULL) { perror(filename); return -1; } while (!err && (fgets( line, 255, fconfig) != NULL)) { key_value_t *key_value ; key_value = file_parse ( line , &err) ; if (err == true) { fclose ( fconfig ) ; fprintf (stderr, "Unable to parse config file(%s)\n", filename ); return 0 ; } else if (key_value == NULL) { /* white or commenet line : skip it */ continue; } if ( ! strcmp( key_value->key , "keyfile") ) { cfgtunnel->keyfile = key_value->value; } else if ( ! strcmp( key_value->key , "certfile") ) { cfgtunnel->certfile = key_value->value; } else if ( ! strcmp( key_value->key , "cacertfile") ) { cfgtunnel->cacertfile = key_value->value; } else if ( ! strcmp( key_value->key , "userfile") ) { cfgtunnel->userfile = key_value->value; } else if ( ! strcmp( key_value->key , "maxclients") ) { cfgtunnel->maxclients = atoi(key_value->value); } else if ( ! strcmp( key_value->key , "port") ) { cfgtunnel->port = atoi(key_value->value); } else if ( ! strcmp( key_value->key , "timeout") ) { cfgtunnel->network_timeout = atoi(key_value->value); } else if ( ! strcmp( key_value->key , "listenaddr") ) { cfgtunnel->listenaddr = key_value->value; } else if ( ! strcmp( key_value->key , "wtmp") ) { cfgtunnel->wtmp = key_value->value; } else if ( ! strcmp( key_value->key , "pidfile") ) { cfgtunnel->pidfile = key_value->value; } else if ( ! strcmp( key_value->key , "lockdir") ) { cfgtunnel->lockdir = key_value->value; } } fclose (fconfig); return err; } static void x509_fingerprint (char *s, int l, X509 * cert) { unsigned char md[EVP_MAX_MD_SIZE]; unsigned int n; int j; if (!X509_digest (cert, EVP_md5 (), md, &n)) { snprintf (s, l, "[unable to calculate]"); } else { for (j = 0; j < (int) n; j++) { char ch[8]; snprintf (ch, 8, "%02X%c", md[j], (j +1 == (int) n ? '\0': ':' )); strncat (s, ch, l); } } } /* Signal handler for forked process */ void sig_int_slave ( int status ) { /* Kill ppp */ if (pppid != -1) kill ( pppid, SIGTERM); else { update_wtmp(cfgtunnel->wtmp, "", getpid(), ""); exit(0); } signal(SIGINT, &sig_int_slave); signal(SIGTERM, &sig_int_slave); } void sig_alrm_slave (int status ) { stopped = 1; signal(SIGALRM, &sig_alrm_slave); } void sig_int_master ( int status ) { stopped = 1; signal ( SIGINT, &sig_int_master ) ; signal ( SIGTERM, &sig_int_master ) ; } void kill_then_wait_childs () { int index; for (index = 0; index < cfgtunnel->maxclients; index ++) { if (*(child_ids + index) != - 1) { syslog( LOG_DEBUG, "Killing %d", *(child_ids + index)); kill ( *(child_ids + index), SIGTERM); } } while (nbchild) { select ( 0 , NULL, NULL, NULL, NULL ); } } static int ssl_accept_timeout(SSL *ssl, int tmo) { int r=0; int rfd, wfd; int n,maxfd; fd_set rfds, wfds; fd_set *pwfds; struct timeval tv; long end; int t; long errcode; rfd = SSL_get_fd(ssl); wfd = SSL_get_fd(ssl); n = rfd + 1; maxfd = (rfd > wfd ? rfd : wfd) + 1; pwfds = (fd_set *) NULL; end = tmo + time( NULL ); tv.tv_sec = tmo; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(rfd,&rfds); /* number of descriptors that changes status */ while (0 < (n = select(n,&rfds,pwfds,(fd_set *) 0,&tv))) { r = SSL_accept(ssl); if (r > 0) { return r; } switch (errcode=SSL_get_error(ssl, r)) { case SSL_ERROR_WANT_READ: pwfds = (fd_set *) 0; n = rfd + 1; break; case SSL_ERROR_WANT_WRITE: pwfds = &wfds; FD_ZERO(&wfds); FD_SET(wfd,&wfds); n = maxfd; break; default: switch (errcode) { case SSL_ERROR_SSL: case SSL_ERROR_SYSCALL: if ((errcode = ERR_get_error()) > 0) { syslog(LOG_NOTICE,"ssl_accept : %s", ERR_error_string(errcode, NULL)); } else { syslog(LOG_NOTICE,"ssl_accept : %d", SSL_get_error(ssl, r)); } break; default: syslog(LOG_NOTICE,"ssl_accept : %d", SSL_get_error(ssl, r)); break; } return -2; } if ((t = end - time( NULL )) < 0) break; tv.tv_sec = t; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(rfd,&rfds); } syslog(LOG_NOTICE,"ssl_accept : timeout"); return -1; } static int ssl_write_timeout(SSL *ssl, char *buffer, int len, int tmo) { int r=0; int rfd, wfd; int n,maxfd; fd_set rfds, wfds; fd_set *prfds; struct timeval tv; long end; int t; rfd = SSL_get_fd(ssl); wfd = SSL_get_fd(ssl); n = rfd + 1; maxfd = (rfd > wfd ? rfd : wfd) + 1; prfds = (fd_set *) NULL; end = tmo + time( NULL ); tv.tv_sec = tmo; tv.tv_usec = 0; FD_ZERO(&wfds); FD_SET(wfd,&wfds); /* number of descriptors that changes status */ while (0 < (n = select(n,prfds,&wfds,(fd_set *) 0,&tv))) { r = SSL_write(ssl, buffer, len); if (r > 0) { return r; } switch (SSL_get_error(ssl, r)) { case SSL_ERROR_WANT_READ: prfds = (fd_set *) &rfds; FD_ZERO(&rfds); FD_SET(rfd,&rfds); n = maxfd; break; case SSL_ERROR_WANT_WRITE: prfds = (fd_set *) NULL; n = wfd + 1; break; default: /* some other error */ syslog(LOG_NOTICE,"ssl_write : %d", SSL_get_error(ssl, r)); return -2; } if ((t = end - time( NULL )) < 0) break; tv.tv_sec = t; tv.tv_usec = 0; FD_ZERO(&wfds); FD_SET(wfd,&wfds); } syslog(LOG_NOTICE,"ssl_write : timeout"); return -1; } static int ssl_read_timeout(SSL *ssl, char *buffer, int len, int tmo) { int r=0; int rfd, wfd; int n,maxfd; fd_set rfds, wfds; fd_set *pwfds; struct timeval tv; long end; int t; rfd = SSL_get_fd(ssl); wfd = SSL_get_fd(ssl); n = rfd + 1; maxfd = (rfd > wfd ? rfd : wfd) + 1; pwfds = (fd_set *) NULL; end = tmo + time( NULL ); tv.tv_sec = tmo; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(rfd,&rfds); /* number of descriptors that changes status */ while (0 < (n = select(n,&rfds,pwfds,(fd_set *) 0,&tv))) { r = SSL_read(ssl, buffer, len); if (r > 0) { return r; } switch (SSL_get_error(ssl, r)) { case SSL_ERROR_WANT_READ: pwfds = (fd_set *) 0; n = rfd + 1; break; case SSL_ERROR_WANT_WRITE: pwfds = &wfds; FD_ZERO(&wfds); FD_SET(wfd,&wfds); n = maxfd; break; default: /* some other error */ syslog(LOG_NOTICE,"ssl_read : %d", SSL_get_error(ssl, r)); return -2; } if ((t = end - time( NULL )) < 0) break; tv.tv_sec = t; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(rfd,&rfds); } syslog(LOG_NOTICE,"ssl_read : timeout"); return -1; } /*# vim:set expandtab:set syntax=c:*/