NOEXPORT void smtp_client(CLI *c) { char *line; line=str_dup(""); do { /* copy multiline greeting */ str_free(line); line=fd_getline(c, c->remote_fd.fd); fd_putline(c, c->local_wfd.fd, line); } while(is_prefix(line, "220-")); fd_putline(c, c->remote_fd.fd, "EHLO localhost"); do { /* skip multiline reply */ str_free(line); line=fd_getline(c, c->remote_fd.fd); } while(is_prefix(line, "250-")); if(!is_prefix(line, "250 ")) { /* error */ s_log(LOG_ERR, "Remote server is not RFC 1425 compliant"); str_free(line); longjmp(c->err, 1); } fd_putline(c, c->remote_fd.fd, "STARTTLS"); do { /* skip multiline reply */ str_free(line); line=fd_getline(c, c->remote_fd.fd); } while(is_prefix(line, "220-")); if(!is_prefix(line, "220 ")) { /* error */ s_log(LOG_ERR, "Remote server is not RFC 2487 compliant"); str_free(line); longjmp(c->err, 1); } str_free(line); }
NOEXPORT void connect_client(CLI *c) { char *line, *encoded; if(!c->opt->protocol_host) { s_log(LOG_ERR, "protocolHost not specified"); longjmp(c->err, 1); } fd_printf(c, c->remote_fd.fd, "CONNECT %s HTTP/1.1", c->opt->protocol_host); fd_printf(c, c->remote_fd.fd, "Host: %s", c->opt->protocol_host); if(c->opt->protocol_username && c->opt->protocol_password) { if(!strcasecmp(c->opt->protocol_authentication, "ntlm")) { #if !defined(OPENSSL_NO_MD4) && OPENSSL_VERSION_NUMBER>=0x0090700fL ntlm(c); #else s_log(LOG_ERR, "NTLM authentication is not available"); longjmp(c->err, 1); #endif } else { /* basic authentication */ line=str_printf("%s:%s", c->opt->protocol_username, c->opt->protocol_password); encoded=base64(1, line, strlen(line)); str_free(line); if(!encoded) { s_log(LOG_ERR, "Base64 encoder failed"); longjmp(c->err, 1); } fd_printf(c, c->remote_fd.fd, "Proxy-Authorization: basic %s", encoded); str_free(encoded); } } fd_putline(c, c->remote_fd.fd, ""); /* empty line */ line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "HTTP/1.0 2") && !is_prefix(line, "HTTP/1.1 2")) { /* not "HTTP/1.x 2xx Connection established" */ s_log(LOG_ERR, "CONNECT request rejected"); do { /* read all headers */ str_free(line); line=fd_getline(c, c->remote_fd.fd); } while(*line); str_free(line); longjmp(c->err, 1); } s_log(LOG_INFO, "CONNECT request accepted"); do { str_free(line); line=fd_getline(c, c->remote_fd.fd); /* read all headers */ } while(*line); str_free(line); }
NOEXPORT void connect_server(CLI *c) { char *request, *proto, *header; NAME_LIST host_list; request=fd_getline(c, c->local_rfd.fd); if(!is_prefix(request, "CONNECT ")) { fd_putline(c, c->local_wfd.fd, "HTTP/1.0 400 Bad Request Method"); fd_putline(c, c->local_wfd.fd, "Server: stunnel/" STUNNEL_VERSION); fd_putline(c, c->local_wfd.fd, ""); str_free(request); longjmp(c->err, 1); } proto=strchr(request+8, ' '); if(!proto || !is_prefix(proto, " HTTP/")) { fd_putline(c, c->local_wfd.fd, "HTTP/1.0 400 Bad Request Protocol"); fd_putline(c, c->local_wfd.fd, "Server: stunnel/" STUNNEL_VERSION); fd_putline(c, c->local_wfd.fd, ""); str_free(request); longjmp(c->err, 1); } *proto='\0'; header=str_dup(""); do { /* ignore any headers */ str_free(header); header=fd_getline(c, c->local_rfd.fd); } while(*header); /* not empty */ str_free(header); host_list.name=request+8; host_list.next=NULL; if(!namelist2addrlist(&c->connect_addr, &host_list, DEFAULT_LOOPBACK)) { fd_putline(c, c->local_wfd.fd, "HTTP/1.0 404 Not Found"); fd_putline(c, c->local_wfd.fd, "Server: stunnel/" STUNNEL_VERSION); fd_putline(c, c->local_wfd.fd, ""); str_free(request); longjmp(c->err, 1); } str_free(request); fd_putline(c, c->local_wfd.fd, "HTTP/1.0 200 OK"); fd_putline(c, c->local_wfd.fd, "Server: stunnel/" STUNNEL_VERSION); fd_putline(c, c->local_wfd.fd, ""); }
NOEXPORT void smtp_server(CLI *c) { char *line; s_poll_init(c->fds); s_poll_add(c->fds, c->local_rfd.fd, 1, 0); switch(s_poll_wait(c->fds, 0, 200)) { /* wait up to 200ms */ case 0: /* fd not ready to read */ s_log(LOG_DEBUG, "RFC 2487 detected"); break; case 1: /* fd ready to read */ s_log(LOG_DEBUG, "RFC 2487 not detected"); return; /* return if RFC 2487 is not used */ default: /* -1 */ sockerror("RFC2487 (s_poll_wait)"); longjmp(c->err, 1); } line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "220")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } fd_printf(c, c->local_wfd.fd, "%s + stunnel", line); str_free(line); line=fd_getline(c, c->local_rfd.fd); if(!is_prefix(line, "EHLO ")) { s_log(LOG_ERR, "Unknown client EHLO"); str_free(line); longjmp(c->err, 1); } fd_printf(c, c->local_wfd.fd, "250-%s Welcome", line); fd_putline(c, c->local_wfd.fd, "250 STARTTLS"); str_free(line); line=fd_getline(c, c->local_rfd.fd); if(!is_prefix(line, "STARTTLS")) { s_log(LOG_ERR, "STARTTLS expected"); str_free(line); longjmp(c->err, 1); } fd_putline(c, c->local_wfd.fd, "220 Go ahead"); str_free(line); }
NOEXPORT void nntp_client(CLI *c) { char *line; line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "200 ") && !is_prefix(line, "201 ")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } fd_putline(c, c->local_wfd.fd, line); fd_putline(c, c->remote_fd.fd, "STARTTLS"); str_free(line); line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "382 ")) { s_log(LOG_ERR, "Server does not support TLS"); str_free(line); longjmp(c->err, 1); } str_free(line); }
gint sock_getline(SockInfo *sock, gchar **line) { g_return_val_if_fail(sock != NULL, -1); g_return_val_if_fail(line != NULL, -1); #if USE_SSL if (sock->ssl) return ssl_getline(sock->ssl, line); #endif return fd_getline(sock->sock, line); }
NOEXPORT void imap_client(CLI *c) { char *line; line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "* OK")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } fd_putline(c, c->local_wfd.fd, line); fd_putline(c, c->remote_fd.fd, "stunnel STARTTLS"); str_free(line); line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "stunnel OK")) { fd_putline(c, c->local_wfd.fd, "* BYE stunnel: Server does not support TLS"); s_log(LOG_ERR, "Server does not support TLS"); str_free(line); longjmp(c->err, 2); /* don't reset */ } str_free(line); }
NOEXPORT void pop3_server(CLI *c) { char *line; line=fd_getline(c, c->remote_fd.fd); fd_printf(c, c->local_wfd.fd, "%s + stunnel", line); str_free(line); line=fd_getline(c, c->local_rfd.fd); if(is_prefix(line, "CAPA")) { /* client wants RFC 2449 extensions */ fd_putline(c, c->local_wfd.fd, "+OK Stunnel capability list follows"); fd_putline(c, c->local_wfd.fd, "STLS"); fd_putline(c, c->local_wfd.fd, "."); str_free(line); line=fd_getline(c, c->local_rfd.fd); } if(!is_prefix(line, "STLS")) { s_log(LOG_ERR, "Client does not want TLS"); str_free(line); longjmp(c->err, 1); } str_free(line); fd_putline(c, c->local_wfd.fd, "+OK Stunnel starts TLS negotiation"); }
void tcpmux (int s, struct servtab *sep) { char service[MAX_SERV_LEN + 1]; int len; /* Get requested service name */ if ((len = fd_getline (s, service, MAX_SERV_LEN)) < 0) { strwrite (s, "-Error reading service name\r\n"); _exit (1); } service[len] = '\0'; if (debug) fprintf (stderr, "tcpmux: someone wants %s\n", service); /* * Help is a required command, and lists available services, * one per line. */ if (!strcasecmp (service, "help")) { for (sep = servtab; sep; sep = sep->se_next) { if (!ISMUX (sep)) continue; write (s, sep->se_service, strlen (sep->se_service)); strwrite (s, "\r\n"); } _exit (1); } /* Try matching a service in inetd.conf with the request */ for (sep = servtab; sep; sep = sep->se_next) { if (ISMUX (sep) && !strcasecmp (service, sep->se_service)) { if (ISMUXPLUS (sep)) { strwrite (s, "+Go\r\n"); } run_service (s, sep); return; } } strwrite (s, "-Service not available\r\n"); exit (1); }
static void auth_user(CLI *c, char *accepted_address) { #ifndef _WIN32_WCE struct servent *s_ent; /* structure for getservbyname */ #endif SOCKADDR_UNION ident; /* IDENT socket name */ char *line, *type, *system, *user; if(!c->opt->username) return; /* -u option not specified */ #ifdef HAVE_STRUCT_SOCKADDR_UN if(c->peer_addr.sa.sa_family==AF_UNIX) { s_log(LOG_INFO, "IDENT not supported on Unix sockets"); return; } #endif c->fd=s_socket(c->peer_addr.sa.sa_family, SOCK_STREAM, 0, 1, "socket (auth_user)"); if(c->fd<0) longjmp(c->err, 1); memcpy(&ident, &c->peer_addr, c->peer_addr_len); #ifndef _WIN32_WCE s_ent=getservbyname("auth", "tcp"); if(s_ent) { ident.in.sin_port=s_ent->s_port; } else #endif { s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ident.in.sin_port=htons(113); } if(connect_blocking(c, &ident, addr_len(&ident))) longjmp(c->err, 1); s_log(LOG_DEBUG, "IDENT server connected"); fd_printf(c, c->fd, "%u , %u", ntohs(c->peer_addr.in.sin_port), ntohs(c->opt->local_addr.in.sin_port)); line=fd_getline(c, c->fd); closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ type=strchr(line, ':'); if(!type) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *type++='\0'; system=strchr(type, ':'); if(!system) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *system++='\0'; if(strcmp(type, " USERID ")) { s_log(LOG_ERR, "Incorrect INETD response type"); str_free(line); longjmp(c->err, 1); } user=strchr(system, ':'); if(!user) { s_log(LOG_ERR, "Malformed IDENT response"); str_free(line); longjmp(c->err, 1); } *user++='\0'; while(*user==' ') /* skip leading spaces */ ++user; if(strcmp(user, c->opt->username)) { safestring(user); s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)", accepted_address, user); str_free(line); longjmp(c->err, 1); } s_log(LOG_INFO, "IDENT authentication passed"); str_free(line); }
NOEXPORT void ntlm(CLI *c) { char *line, buf[BUFSIZ], *ntlm1_txt, *ntlm2_txt, *ntlm3_txt, *tmpstr; long content_length=0; /* no HTTP content */ /* send Proxy-Authorization (phase 1) */ fd_printf(c, c->remote_fd.fd, "Proxy-Connection: keep-alive"); ntlm1_txt=ntlm1(); if(!ntlm1_txt) { s_log(LOG_ERR, "Proxy-Authenticate: Failed to build NTLM request"); longjmp(c->err, 1); } fd_printf(c, c->remote_fd.fd, "Proxy-Authorization: NTLM %s", ntlm1_txt); str_free(ntlm1_txt); fd_putline(c, c->remote_fd.fd, ""); /* empty line */ line=fd_getline(c, c->remote_fd.fd); /* receive Proxy-Authenticate (phase 2) */ if(!is_prefix(line, "HTTP/1.0 407") && !is_prefix(line, "HTTP/1.1 407")) { s_log(LOG_ERR, "Proxy-Authenticate: NTLM authorization request rejected"); do { /* read all headers */ str_free(line); line=fd_getline(c, c->remote_fd.fd); } while(*line); str_free(line); longjmp(c->err, 1); } ntlm2_txt=NULL; do { /* read all headers */ str_free(line); line=fd_getline(c, c->remote_fd.fd); if(is_prefix(line, "Proxy-Authenticate: NTLM ")) ntlm2_txt=str_dup(line+25); else if(is_prefix(line, "Content-Length: ")) { content_length=strtol(line+16, &tmpstr, 10); if(tmpstr==line+16 || *tmpstr || content_length<0) { s_log(LOG_ERR, "Proxy-Authenticate: Invalid Content-Length"); str_free(line); longjmp(c->err, 1); } } } while(*line); if(!ntlm2_txt) { /* no Proxy-Authenticate: NTLM header */ s_log(LOG_ERR, "Proxy-Authenticate: NTLM header not found"); str_free(line); longjmp(c->err, 1); } /* read and ignore HTTP content (if any) */ while(content_length>0) { s_read(c, c->remote_fd.fd, buf, s_min(content_length, BUFSIZ)); content_length-=s_min(content_length, BUFSIZ); } /* send Proxy-Authorization (phase 3) */ fd_printf(c, c->remote_fd.fd, "CONNECT %s HTTP/1.1", c->opt->protocol_host); fd_printf(c, c->remote_fd.fd, "Host: %s", c->opt->protocol_host); ntlm3_txt=ntlm3(c->opt->protocol_username, c->opt->protocol_password, ntlm2_txt); str_free(ntlm2_txt); if(!ntlm3_txt) { s_log(LOG_ERR, "Proxy-Authenticate: Failed to build NTLM response"); longjmp(c->err, 1); } fd_printf(c, c->remote_fd.fd, "Proxy-Authorization: NTLM %s", ntlm3_txt); str_free(ntlm3_txt); }
NOEXPORT void imap_server(CLI *c) { char *line, *id, *tail, *capa; s_poll_init(c->fds); s_poll_add(c->fds, c->local_rfd.fd, 1, 0); switch(s_poll_wait(c->fds, 0, 200)) { case 0: /* fd not ready to read */ s_log(LOG_DEBUG, "RFC 2595 detected"); break; case 1: /* fd ready to read */ s_log(LOG_DEBUG, "RFC 2595 not detected"); return; /* return if RFC 2595 is not used */ default: /* -1 */ sockerror("RFC2595 (s_poll_wait)"); longjmp(c->err, 1); } /* process server welcome and send it to client */ line=fd_getline(c, c->remote_fd.fd); if(!is_prefix(line, "* OK")) { s_log(LOG_ERR, "Unknown server welcome"); str_free(line); longjmp(c->err, 1); } capa=strstr(line, "CAPABILITY"); if(!capa) capa=strstr(line, "capability"); if(capa) *capa='K'; /* disable CAPABILITY within greeting */ fd_printf(c, c->local_wfd.fd, "%s (stunnel)", line); id=str_dup(""); while(1) { /* process client commands */ str_free(line); line=fd_getline(c, c->local_rfd.fd); /* split line into id and tail */ str_free(id); id=str_dup(line); tail=strchr(id, ' '); if(!tail) break; *tail++='\0'; if(is_prefix(tail, "STARTTLS")) { fd_printf(c, c->local_wfd.fd, "%s OK Begin TLS negotiation now", id); str_free(line); str_free(id); return; /* success */ } else if(is_prefix(tail, "CAPABILITY")) { fd_putline(c, c->remote_fd.fd, line); /* send it to server */ str_free(line); line=fd_getline(c, c->remote_fd.fd); /* get the capabilites */ if(*line=='*') { /* * append STARTTLS * should also add LOGINDISABLED, but can't because * of Mozilla bug #324138/#312009 * LOGIN would fail as "unexpected command", anyway */ fd_printf(c, c->local_wfd.fd, "%s STARTTLS", line); str_free(line); line=fd_getline(c, c->remote_fd.fd); /* next line */ } fd_putline(c, c->local_wfd.fd, line); /* forward to the client */ tail=strchr(line, ' '); if(!tail || !is_prefix(tail+1, "OK")) { /* not OK? */ fd_putline(c, c->local_wfd.fd, "* BYE unexpected server response"); s_log(LOG_ERR, "Unexpected server response: %s", line); break; } } else if(is_prefix(tail, "LOGOUT")) { fd_putline(c, c->local_wfd.fd, "* BYE server terminating"); fd_printf(c, c->local_wfd.fd, "%s OK LOGOUT completed", id); break; } else { fd_putline(c, c->local_wfd.fd, "* BYE stunnel: unexpected command"); fd_printf(c, c->local_wfd.fd, "%s BAD %s unexpected", id, tail); s_log(LOG_ERR, "Unexpected client command %s", tail); break; } } /* clean server shutdown */ str_free(id); fd_putline(c, c->remote_fd.fd, "stunnel LOGOUT"); str_free(line); line=fd_getline(c, c->remote_fd.fd); if(*line=='*') { str_free(line); line=fd_getline(c, c->remote_fd.fd); } str_free(line); longjmp(c->err, 2); /* don't reset */ }