static OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req) { BIO *bio=NULL; OCSP_REQ_CTX *req_ctx=NULL; OCSP_RESPONSE *resp=NULL; int err; /* connect specified OCSP server (responder) */ c->fd=s_socket(c->opt->ocsp_addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket (auth_user)"); if(c->fd<0) goto cleanup; if(connect_blocking(c, &c->opt->ocsp_addr, addr_len(&c->opt->ocsp_addr))) goto cleanup; bio=BIO_new_fd(c->fd, BIO_NOCLOSE); if(!bio) goto cleanup; s_log(LOG_DEBUG, "OCSP: server connected"); /* OCSP protocol communication loop */ req_ctx=OCSP_sendreq_new(bio, c->opt->ocsp_path, req, -1); if(!req_ctx) { sslerror("OCSP: OCSP_sendreq_new"); goto cleanup; } while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) { s_poll_init(c->fds); s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio)); err=s_poll_wait(c->fds, c->opt->timeout_busy, 0); if(err==-1) sockerror("OCSP: s_poll_wait"); if(err==0) s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded"); if(err<=0) goto cleanup; } /* s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); */ /* http://www.mail-archive.com/[email protected]/msg61691.html */ if(!resp) { if(ERR_peek_error()) sslerror("OCSP: OCSP_sendreq_nbio"); else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */ s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error"); } cleanup: if(req_ctx) OCSP_REQ_CTX_free(req_ctx); if(bio) BIO_free_all(bio); if(c->fd>=0) { closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ } return resp; }
static void make_sockets(CLI *c, int fd[2]) { /* make a pair of connected sockets */ #ifdef INET_SOCKET_PAIR SOCKADDR_UNION addr; socklen_t addrlen; int s; /* temporary socket awaiting for connection */ s=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#1"); if(s<0) longjmp(c->err, 1); c->fd=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#2"); if(c->fd<0) longjmp(c->err, 1); addrlen=sizeof addr; memset(&addr, 0, addrlen); addr.in.sin_family=AF_INET; addr.in.sin_addr.s_addr=htonl(INADDR_LOOPBACK); addr.in.sin_port=htons(0); /* dynamic port allocation */ if(bind(s, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#1"); if(bind(c->fd, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#2"); if(listen(s, 1)) { closesocket(s); sockerror("listen"); longjmp(c->err, 1); } if(getsockname(s, &addr.sa, &addrlen)) { closesocket(s); sockerror("getsockname"); longjmp(c->err, 1); } if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(s); longjmp(c->err, 1); } fd[0]=s_accept(s, &addr.sa, &addrlen, 1, "accept"); if(fd[0]<0) { closesocket(s); longjmp(c->err, 1); } fd[1]=c->fd; c->fd=-1; closesocket(s); /* don't care about the result */ #else if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, fd, 1, "socketpair")) longjmp(c->err, 1); #endif }
static int connect_remote(CLI *c) { /* connect remote host */ SOCKADDR_UNION addr; SOCKADDR_LIST resolved_list, *address_list; int fd, ind_try, ind_cur; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(ind_try=0; ind_try<address_list->num; ind_try++) { if(c->opt->failover==FAILOVER_RR) { ind_cur=address_list->cur; /* the race condition here can be safely ignored */ address_list->cur=(ind_cur+1)%address_list->num; } else { /* FAILOVER_PRIO */ ind_cur=ind_try; /* ignore address_list->cur */ } memcpy(&addr, address_list->addr+ind_cur, sizeof addr); c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); if(c->bind_addr.num) /* explicit local bind or transparent proxy */ local_bind(c); if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
static int connect_transparent(CLI *c) { /* connect the original dst */ SOCKADDR_UNION addr; socklen_t addrlen=sizeof addr; int retval; if(getsockopt(c->local_rfd.fd, SOL_IP, SO_ORIGINAL_DST, &addr, &addrlen)) { sockerror("setsockopt SO_ORIGINAL_DST"); longjmp(c->err, 1); } c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); if(c->bind_addr.num) /* explicit local bind or transparent proxy */ local_bind(c); if(connect_blocking(c, &addr, addr_len(addr))) longjmp(c->err, 1); /* socket closed on cleanup */ print_bound_address(c); retval=c->fd; c->fd=-1; return retval; /* success! */ }
static int connect_remote(CLI * c) { int fd, ind_try, ind_cur; SOCKADDR_LIST *remote_addr; remote_addr = dynamic_remote_addr(c); for (ind_try = 0; ind_try < remote_addr->num; ind_try++) { if (c->opt->failover == FAILOVER_RR) { ind_cur = remote_addr->cur; remote_addr->cur = (ind_cur + 1) % remote_addr->num; } else { ind_cur = ind_try; } c->fd = s_socket(remote_addr->addr[ind_cur].sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if (c->fd < 0) longjmp(c->err, 1); local_bind(c); if (connect_blocking(c, &remote_addr->addr[ind_cur], addr_len(&remote_addr->addr[ind_cur]))) { closesocket(c->fd); c->fd = -1; continue; } print_bound_address(c); fd = c->fd; c->fd = -1; return fd; } longjmp(c->err, 1); return -1; }
/* connect remote host */ static int connect_remote(CLI *c) { int fd, ind_try, ind_cur; SOCKADDR_LIST *remote_addr; /* list of connect_blocking() targets */ remote_addr=dynamic_remote_addr(c); /* try to connect each host from the list */ for(ind_try=0; ind_try<remote_addr->num; ind_try++) { if(c->opt->failover==FAILOVER_RR) { ind_cur=remote_addr->cur; /* the race condition here can be safely ignored */ remote_addr->cur=(ind_cur+1)%remote_addr->num; } else { /* FAILOVER_PRIO */ ind_cur=ind_try; /* ignore remote_addr->cur */ } c->fd=s_socket(remote_addr->addr[ind_cur].sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); local_bind(c); /* explicit local bind or transparent proxy */ if(connect_blocking(c, &remote_addr->addr[ind_cur], addr_len(&remote_addr->addr[ind_cur]))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
static void auth_user(CLI *c) { #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 */ c->fd=s_socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0, 1, "socket (auth_user)"); if(c->fd<0) longjmp(c->err, 1); memcpy(&ident, &c->peer_addr.addr[0], sizeof ident); #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"); fdprintf(c, c->fd, "%u , %u", ntohs(c->peer_addr.addr[0].in.sin_port), ntohs(c->opt->local_addr.addr[0].in.sin_port)); line=fdgetline(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)", c->accepted_address, user); str_free(line); longjmp(c->err, 1); } s_log(LOG_INFO, "IDENT authentication passed"); str_free(line); }
static int ocsp_check(CLI *c, X509_STORE_CTX *callback_ctx) { int error, retval=0; SOCKADDR_UNION addr; X509 *cert; X509 *issuer=NULL; OCSP_CERTID *certID; BIO *bio=NULL; OCSP_REQUEST *request=NULL; OCSP_RESPONSE *response=NULL; OCSP_BASICRESP *basicResponse=NULL; ASN1_GENERALIZEDTIME *revoked_at=NULL, *this_update=NULL, *next_update=NULL; int status, reason; /* connect specified OCSP server (responder) */ c->fd=s_socket(c->opt->ocsp_addr.addr[0].sa.sa_family, SOCK_STREAM, 0, 0, "OCSP: socket (auth_user)"); if(c->fd<0) return 0; /* reject connection */ memcpy(&addr, &c->opt->ocsp_addr.addr[0], sizeof addr); if(connect_blocking(c, &addr, addr_len(addr))) goto cleanup; s_log(LOG_DEBUG, "OCSP: server connected"); /* get current certificate ID */ cert=X509_STORE_CTX_get_current_cert(callback_ctx); /* get current cert */ if(X509_STORE_CTX_get1_issuer(&issuer, callback_ctx, cert)!=1) { sslerror("OCSP: X509_STORE_CTX_get1_issuer"); goto cleanup; } certID=OCSP_cert_to_id(0, cert, issuer); if(!certID) { sslerror("OCSP: OCSP_cert_to_id"); goto cleanup; } /* build request */ request=OCSP_REQUEST_new(); if(!request) { sslerror("OCSP: OCSP_REQUEST_new"); goto cleanup; } if(!OCSP_request_add0_id(request, certID)) { sslerror("OCSP: OCSP_request_add0_id"); goto cleanup; } OCSP_request_add1_nonce(request, 0, -1); /* send the request and get a response */ /* FIXME: this code won't work with ucontext threading */ /* (blocking sockets are used) */ bio=BIO_new_fd(c->fd, BIO_NOCLOSE); response=OCSP_sendreq_bio(bio, c->opt->ocsp_path, request); if(!response) { sslerror("OCSP: OCSP_sendreq_bio"); goto cleanup; } error=OCSP_response_status(response); if(error!=OCSP_RESPONSE_STATUS_SUCCESSFUL) { s_log(LOG_WARNING, "OCSP: Responder error: %d: %s", error, OCSP_response_status_str(error)); goto cleanup; } s_log(LOG_DEBUG, "OCSP: Response received"); /* verify the response */ basicResponse=OCSP_response_get1_basic(response); if(!basicResponse) { sslerror("OCSP: OCSP_response_get1_basic"); goto cleanup; } if(OCSP_check_nonce(request, basicResponse)<=0) { sslerror("OCSP: OCSP_check_nonce"); goto cleanup; } if(OCSP_basic_verify(basicResponse, NULL, c->opt->revocation_store, c->opt->ocsp_flags)<=0) { sslerror("OCSP: OCSP_basic_verify"); goto cleanup; } if(!OCSP_resp_find_status(basicResponse, certID, &status, &reason, &revoked_at, &this_update, &next_update)) { sslerror("OCSP: OCSP_resp_find_status"); goto cleanup; } s_log(LOG_NOTICE, "OCSP: Status: %d: %s", status, OCSP_cert_status_str(status)); log_time(LOG_INFO, "OCSP: This update", this_update); log_time(LOG_INFO, "OCSP: Next update", next_update); /* check if the response is valid for at least one minute */ if(!OCSP_check_validity(this_update, next_update, 60, -1)) { sslerror("OCSP: OCSP_check_validity"); goto cleanup; } if(status==V_OCSP_CERTSTATUS_REVOKED) { if(reason==-1) s_log(LOG_WARNING, "OCSP: Certificate revoked"); else s_log(LOG_WARNING, "OCSP: Certificate revoked: %d: %s", reason, OCSP_crl_reason_str(reason)); log_time(LOG_NOTICE, "OCSP: Revoked at", revoked_at); goto cleanup; } retval=1; /* accept connection */ cleanup: if(bio) BIO_free_all(bio); if(issuer) X509_free(issuer); if(request) OCSP_REQUEST_free(request); if(response) OCSP_RESPONSE_free(response); if(basicResponse) OCSP_BASICRESP_free(basicResponse); closesocket(c->fd); c->fd=-1; /* avoid double close on cleanup */ return retval; }
static void auth_user(CLI * c, char *accepted_address) { struct servent *s_ent; SOCKADDR_UNION ident; char *line, *type, *system, *user; if (!c->opt->username) return; if (c->peer_addr.sa.sa_family == AF_UNIX) { s_log(LOG_INFO, "IDENT not supported on Unix sockets"); return; } 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); s_ent = getservbyname("auth", "tcp"); if (s_ent) { ident.in.sin_port = s_ent->s_port; } else { 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; 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 == ' ') ++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); }