diff -u stunnel-3.22/client.c stunnel-3.22-sg/client.c --- stunnel-3.22/client.c Sun Dec 23 14:41:32 2001 +++ stunnel-3.22-sg/client.c Wed May 29 02:51:34 2002 @@ -220,7 +220,9 @@ else c->ip=0; /* Setup c->remote_fd, now */ - if(options.option&OPT_REMOTE) + if (options.option&OPT_FDS) + fd=options.use_fd; + else if(options.option&OPT_REMOTE) fd=connect_remote(c); else /* NOT in remote mode */ fd=connect_local(c); @@ -845,9 +847,9 @@ int fdprintf(int fd, char *format, ...) { va_list arglist; - char line[STRLEN], logline[STRLEN]; + char line[STRLEN]; + int len; char crlf[]="\r\n"; - int len, ptr, written, towrite; va_start(arglist, format); #ifdef HAVE_VSNPRINTF @@ -858,58 +860,87 @@ va_end(arglist); safeconcat(line, crlf); len+=2; - for(ptr=0, towrite=len; towrite>0; ptr+=written, towrite-=written) { - switch(waitforsocket(fd, 1 /* write */)) { - case -1: /* Error */ - sockerror("select"); - return -1; - case 0: /* Timeout */ - log(LOG_ERR, "Select timeout (fdprintf)"); - return -1; - } - written=writesocket(fd, line+ptr, towrite); - if(written<0) { - sockerror("writesocket (fdprintf)"); - return -1; - } - } - safecopy(logline, line); - safestring(logline); - log(LOG_DEBUG, " -> %s", logline); - return len; + if (fdwrite(fd,line,len) < 0) + return -1; + else + return len; } -int fdscanf(int fd, char *format, char *buffer) { - char line[STRLEN], logline[STRLEN]; +int fdputs(int fd, char *line) { + char crlf[]="\r\n"; + + if (fdwrite(fd, line, strlen(line)) < 0) + return -1; + return fdwrite(fd,crlf,strlen(crlf)); +} + +int fdwrite(int fd, char *buffer, int len) { + char logline[STRLEN]; + int ptr, written, towrite; + + for(ptr=0, towrite=len; towrite>0; ptr+=written, towrite-=written) { + switch(waitforsocket(fd, 1 /* write */)) { + case -1: /* Error */ + sockerror("select"); + return -1; + case 0: /* Timeout */ + log(LOG_ERR, "Select timeout (fdwrite)"); + return -1; + } + + written=writesocket(fd, buffer+ptr, towrite); + if(written<0) { + sockerror("writesocket (fdwrite)"); + return -1; + } + } + safecopy(logline, buffer); + safestring(logline); + log(LOG_DEBUG, " -> %s", logline); + return 0; +} + +int fdgets(int fd, char *buffer) { + char logline[STRLEN]; int ptr; - for(ptr=0; ptrremote_fd; + if (c->local_wfd > fdmax) + fdmax = c->local_wfd; + if (c->local_rfd > fdmax) + fdmax = c->local_rfd; + fdmax++; + + lbufend = rbufend = 0; + rdone = ldone = 0; + + tv.tv_sec = 86400; + tv.tv_usec = 0; + while( !( (rdone || ldone) && (!rbufend && !lbufend) ) ) { + log(LOG_DEBUG,"rdone=%d, ldone=%d, rbufend=%d, lbufend=%d",rdone,ldone,rbufend,lbufend); + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + if (rbufend) + FD_SET((c->local_wfd),&wr_set); + else if (!rdone) + FD_SET(c->remote_fd,&rd_set); + + if (lbufend) + FD_SET(c->remote_fd,&wr_set); + else if (!ldone) + FD_SET(c->local_rfd,&rd_set); + + do { /* Skip "Interrupted system call" errors */ + ready=select(fdmax, &rd_set, &wr_set, NULL, &tv); + } while(ready<0 && get_last_socket_error()==EINTR); + + if(ready<0) { /* Break the connection for others */ + sockerror("select"); + c->error=1; + return 1; + } + if(!ready) { /* Timeout */ + log(LOG_DEBUG, "select timeout - connection reset"); + c->error=1; + return 1; + } + + if (lbufend > 0) { + if (FD_ISSET(c->remote_fd,&wr_set)) + { + sofar = 0; + while (sofar < lbufend) { + if ((howmuch=write(c->remote_fd,lbuf+sofar,lbufend-sofar)) < 0) { + sockerror("write"); + c->error=1; + return 1; + } + sofar += howmuch; + } + lbufend = 0; + } + } else { + if (FD_ISSET(c->local_rfd,&rd_set)) { + if ( (lbufend = read(c->local_rfd,lbuf,PP_BUFSIZE)) < 0) { + sockerror("read"); + c->error = 1; + return 1; + } + if (lbufend == 0) /* EOF */ + { + log(LOG_DEBUG,"EOF reading from local"); + ldone=1; + } + } + } + + if (rbufend > 0) { + if (FD_ISSET(c->local_wfd, &wr_set)) { + sofar = 0; + while (sofar < rbufend) { + if ( (howmuch = write(c->local_wfd, rbuf + sofar, rbufend - sofar)) < 0) { + sockerror("write"); + c->error=1; + return 1; + } + sofar += howmuch; + } + rbufend = 0; + } + } else { + if (FD_ISSET(c->remote_fd,&rd_set)) { + if ( (rbufend = read(c->remote_fd,rbuf,PP_BUFSIZE)) < 0) { + sockerror("read"); + c->error = 1; + return 1; + } + if (rbufend == 0) /* EOF */ { + log(LOG_DEBUG,"EOF reading from remote"); + rdone=1; + } + } + } + } + + log(LOG_DEBUG, "plaintext_proxy finished normally."); + return 0; +} + +static int smtp_server(CLI *c, int allow_downgrade) { char line[STRLEN]; + int read_ret; if(RFC2487(c->local_rfd)==0) return 0; /* Return if RFC 2487 is not used */ - if(fdscanf(c->remote_fd, "220%[^\n]", line)!=1) { + if(fdscanf(c->remote_fd, "220%[^\r\n]", line)!=1) { log(LOG_ERR, "Unknown server welcome"); return -1; } if(fdprintf(c->local_wfd, "220%s + stunnel", line)<0) return -1; - if(fdscanf(c->local_rfd, "EHLO %[^\n]", line)!=1) { - log(LOG_ERR, "Unknown client EHLO"); - return -1; - } - if(fdprintf(c->local_wfd, "250-%s Welcome", line)<0) - return -1; - if(fdprintf(c->local_wfd, "250 STARTTLS")<0) - return -1; - if(fdscanf(c->local_rfd, "STARTTLS", line)<0) { - log(LOG_ERR, "STARTTLS expected"); - return -1; + + /* See if we get an EHLO command */ + if (((read_ret=fdgets(c->local_rfd, line)) >= 0) && + (strlen(line) >= 4) && + (!line[4] || isspace(line[4])) && + (strncasecmp(line,"ehlo",4) == 0) ) { + if (fdputs(c->remote_fd,line) < 0) + return -1; + while ( (fdgets(c->remote_fd, line) >= 0) + && (line[3] == '-') ) { + if (fdputs(c->local_wfd,line) < 0) + return -1; + } + line[3] = '-'; + if (fdputs(c->local_wfd,line) < 0) + return -1; + if (fdputs(c->local_wfd,"250 STARTTLS") < 0) + return -1; + + /* See if we get a STARTTLS command */ + if (((read_ret=fdgets(c->local_rfd, line)) >= 0) && + (strlen(line) >= 8) && + (!line[8] || isspace(line[8])) && + (strncasecmp(line,"starttls",8) == 0) ) { + if (!line[8]) { + /* Technically, we should shut down the SMTP connection and get + * a new one, but screw it for now. */ + if(fdputs(c->local_wfd, "220 Go ahead") >= 0) + return 0; + } else { + fdputs(c->local_wfd,"501 Syntax error (no parameters allowed; STARTTLS disabled) (#5.5.4)"); + return -1; + } + + } + } + if (read_ret < 0) + return -1; + if (allow_downgrade) { + if (fdputs(c->remote_fd,line) < 0) + return -1; + exit(plaintext_proxy(c)); + } + else { + fdputs(c->local_wfd,"421 Encryption required; must use STARTTLS"); + return -1; } - if(fdprintf(c->local_wfd, "220 Go ahead", line)<0) - return -1; - return 0; } static int pop3_client(CLI *c) { @@ -182,31 +350,157 @@ return 0; } -static int pop3_server(CLI *c) { +static int pop3_server(CLI *c, int allow_downgrade) { char line[STRLEN]; - if(fdscanf(c->remote_fd, "%[^\n]", line)<0) - return -1; + if(fdscanf(c->remote_fd, "%[^\r\n]", line)<0) + return -1; + if(fdprintf(c->local_wfd, "%s + stunnel", line)<0) return -1; - if(fdscanf(c->local_rfd, "%[^\n]", line)<0) + + while(1) { + if(fdgets(c->local_rfd, line)<0) return -1; - if(!strncmp(line, "CAPA", 4)) { /* Client wants RFC 2449 extensions */ - if(fdprintf(c->local_wfd, "-ERR Stunnel does not support capabilities")<0) - return -1; - if(fdscanf(c->local_rfd, "%[^\n]", line)<0) - return -1; + if(strncasecmp(line, "CAPA", 4) == 0) { /* Client wants RFC 2449 extensions */ + fdputs(c->remote_fd,line); + if (fdgets(c->remote_fd,line) < 0) + return -1; + if (line[0] == '+') { + if (fdputs(c->local_wfd,line) < 0) + return -1; + while ( (fdgets(c->remote_fd,line) >= 0) + && ( (strlen(line) > 1) || (line[0] != '.') ) ) { + if (fdputs(c->local_wfd,line) < 0) + return -1; + } + } else { + if (fdputs(c->local_wfd,"+OK Stunnel capability list follows") < 0) + return -1; + } + if (fdputs(c->local_wfd,"STLS") < 0) + return -1; + if (fdputs(c->local_wfd,".") < 0) + return -1; + } else if (strncasecmp(line, "STLS", 4) == 0) { + if(fdputs(c->local_wfd, "+OK stunnel starting TLS negotiation")<0) + return -1; + return 0; + } + else { + if (fdputs(c->remote_fd,line) < 0) + return -1; + if (allow_downgrade) + exit(plaintext_proxy(c)); + else { + fdputs(c->local_wfd,"-Encryption required; must use STLS"); + return -1; + } + } } - if(strncmp(line, "STLS", 4)) { - log(LOG_ERR, "Client does not want TLS"); +} + +static int imap_client(CLI *c) { + char line[STRLEN]; + + if(fdgets(c->remote_fd, line)<0) + return -1; + if(strncasecmp(line,"* OK ",4)) { + log(LOG_ERR, "Unknown server welcome"); return -1; } - if(fdprintf(c->local_wfd, "+OK Stunnel starts TLS negotiation")<0) + if(fdputs(c->local_wfd, line)<0) return -1; - + if(fdprintf(c->remote_fd, "a STARTTLS")<0) + return -1; + if(fdgets(c->remote_fd, line)<0) + return -1; + if(strncasecmp(line,"a OK ",5)) { + log(LOG_ERR, "Server does not support TLS"); + return -1; + } return 0; } +static int imap_server(CLI *c, int allow_downgrade) { + char line[STRLEN]; + char procline[STRLEN]; + char *tag, *cmd; + int do_if_fail = 0; + + if (fdgets(c->remote_fd,line) < 0) + return -1; + + if(fdprintf(c->local_wfd, "%s + stunnel", line)<0) + return -1; + + while(1) { + if(fdgets(c->local_rfd, line)<0) + return -1; + safecopy(procline, line); + tag = procline; + if (!(cmd=strchr(procline,' '))) { + do_if_fail = 1; + break; + } + *cmd='\0'; + cmd++; + if(strncasecmp(cmd, "CAPABILITY", 11) == 0) { + fdputs(c->remote_fd,line); + if (fdgets(c->remote_fd,line) < 0) + return -1; + if (strncasecmp(line,"* CAPABILITY",12) == 0) { + safeconcat(line," STARTTLS"); + fdputs(c->local_wfd,line); + } else { + do_if_fail = 2; + break; + } + if (fdgets(c->remote_fd,line) < 0) + return -1; + if ( (strncasecmp(line,tag,strlen(tag)) != 0) || + (line[strlen(tag)] != ' ') || + (strncasecmp(line+strlen(tag)+1,"OK",2) != 0) ) + { + do_if_fail = 2; + break; + } + if (fdputs(c->local_wfd,line) < 0) + return -1; + } + else if (strncasecmp(cmd, "STARTTLS", 8) == 0) { + if (fdwrite(c->local_wfd,tag,strlen(tag)) < 0) + return -1; + if (fdwrite(c->local_wfd," ",1) < 0) + return -1; + if (fdputs(c->local_wfd,"OK Begin TLS negotiation now") < 0) + return -1; + + return 0; + } else { + do_if_fail = 1; + break; + } + } + + if (allow_downgrade) { + if (do_if_fail == 1) { + if (fdputs(c->remote_fd,line) < 0) + return -1; + } else if (do_if_fail == 2) { + if (fdputs(c->local_wfd,line) < 0) + return -1; + } + exit(plaintext_proxy(c)); + } + else { + fdputs(c->local_wfd,"-Encryption required; must use STLS"); + return -1; + } + +} + + static int nntp_client(CLI *c) { char line[STRLEN]; diff -u stunnel-3.22/prototypes.h stunnel-3.22-sg/prototypes.h --- stunnel-3.22/prototypes.h Sun Nov 11 14:16:01 2001 +++ stunnel-3.22-sg/prototypes.h Wed May 29 02:15:43 2002 @@ -41,7 +41,10 @@ /* Prototypes for client.c */ void *client(void *); /* descriptor versions of fprintf/fscanf */ +int fdwrite(int fd, char *buffer, int len); +int fdputs(int fd, char *line); int fdprintf(int, char *, ...); +int fdgets(int fd, char *buffer); int fdscanf(int, char *, char *); /* Prototypes for log.c */ @@ -79,6 +82,8 @@ #define OPT_REMOTE 0x20 #define OPT_TRANSPARENT 0x40 #define OPT_PTY 0x80 +#define OPT_FDS 0x100 +#define OPT_SUID_FIRST 0x200 typedef struct { char pem[STRLEN]; /* pem (priv key/cert) filename */ @@ -111,6 +116,8 @@ int cert_defaults; char *output_file; u32 *local_ip; + int use_fd; + char *chroot_dir; } server_options; typedef enum { Only in stunnel-3.22-sg: prototypes.h~ Only in stunnel-3.22-sg: pty.o diff -u stunnel-3.22/ssl.c stunnel-3.22-sg/ssl.c --- stunnel-3.22/ssl.c Sun Dec 23 14:46:03 2001 +++ stunnel-3.22-sg/ssl.c Wed May 15 03:54:39 2002 @@ -46,6 +46,7 @@ #include #include #include +#include #else #include #include @@ -71,7 +72,6 @@ SSL_CTX *ctx; /* global SSL context */ void context_init() { /* init SSL */ - if(!init_prng()) log(LOG_INFO, "PRNG seeded successfully"); SSLeay_add_ssl_algorithms(); Only in stunnel-3.22-sg: ssl.c~ Only in stunnel-3.22-sg: ssl.o Only in stunnel-3.22-sg: sthreads.o Only in stunnel-3.22-sg: stunnel diff -u stunnel-3.22/stunnel.c stunnel-3.22-sg/stunnel.c --- stunnel-3.22/stunnel.c Thu Dec 20 02:53:54 2001 +++ stunnel-3.22-sg/stunnel.c Tue May 28 04:03:39 2002 @@ -54,6 +54,8 @@ static void create_pid(); static void delete_pid(); #endif +void do_setugid(char *user, char *group); +int is_alldigits(char *s); /* Socket functions */ static int listen_local(); @@ -119,6 +121,24 @@ if(!(options.option&OPT_FOREGROUND)) options.foreground=0; log_open(); + if (options.chroot_dir) + { + log(LOG_DEBUG,"chroot(%s)",options.chroot_dir); + if (chroot(options.chroot_dir) < 0) + { + ioerror("chroot"); + exit(1); + } + + if (chdir("/") < 0) + { + ioerror("chdir"); + exit(1); + } + } + if ( options.option&OPT_SUID_FIRST && + (options.setuid_user || options.setgid_group)) + do_setugid(options.setuid_user,options.setgid_group); log(LOG_NOTICE, "Using '%s' as tcpwrapper service name", options.servname); /* check if certificate exists */ @@ -371,49 +391,9 @@ exit(1); } -#ifndef USE_WIN32 - if(options.setgid_group) { - struct group *gr; - gid_t gr_list[1]; - - gr=getgrnam(options.setgid_group); - if(!gr) { - log(LOG_ERR, "Failed to get GID for group %s", - options.setgid_group); - exit(1); - } - if(setgid(gr->gr_gid)) { - sockerror("setgid"); - exit(1); - } - gr_list[0]=gr->gr_gid; - if(setgroups(1, gr_list)) { - sockerror("setgroups"); - exit(1); - } - } - - if(options.setuid_user) { - struct passwd *pw; - - pw=getpwnam(options.setuid_user); - if(!pw) { - log(LOG_ERR, "Failed to get UID for user %s", - options.setuid_user); - exit(1); - } -#ifndef USE_WIN32 - /* gotta chown that pid file, or we can't remove it. */ - if ( options.pidfile[0] && chown( options.pidfile, pw->pw_uid, -1) ) { - log(LOG_ERR, "Failed to chown pidfile %s", options.pidfile); - } -#endif - if(setuid(pw->pw_uid)) { - sockerror("setuid"); - exit(1); - } - } -#endif /* USE_WIN32 */ + if (!(options.option&OPT_SUID_FIRST) && + (options.setuid_user || options.setgid_group)) + do_setugid(options.setuid_user,options.setgid_group); return ls; } @@ -682,4 +662,79 @@ return retval; } +void do_setugid(char *user, char *group) + { + uid_t uid; + gid_t gid; + +#ifndef USE_WIN32 + if(group) { + struct group *gr; + gid_t gr_list[1]; + + if (is_alldigits(group)) + { + gid=atoi(group); + } + else + { + gr=getgrnam(group); + if(!gr) { + log(LOG_ERR, "Failed to get GID for group %s", + group); + exit(1); + } + gid = gr->gr_gid; + } + + if(setgid(gid)) { + sockerror("setgid"); + exit(1); + } + gr_list[0]=gid; + if(setgroups(1, gr_list)) { + sockerror("setgroups"); + exit(1); + } + } +#endif + + if(user) { + struct passwd *pw; + + if (is_alldigits(user)) + { + uid = atoi(user); + } + else + { + pw=getpwnam(user); + if(!pw) { + log(LOG_ERR, "Failed to get UID for user %s", + user); + exit(1); + } + uid=pw->pw_uid; + } + +#ifndef USE_WIN32 + /* gotta chown that pid file, or we can't remove it. */ + if ( options.pidfile[0] && chown( options.pidfile, uid, -1) ) { + log(LOG_ERR, "Failed to chown pidfile %s", options.pidfile); + } +#endif + if(setuid(uid)) { + sockerror("setuid"); + exit(1); + } + } + } + +int is_alldigits(char *s) { + while (*s) + if (!isdigit(*s++)) + return 0; + return 1; +} + /* End of stunnel.c */