int32 matrixSslNewServerSession(ssl_t **ssl, const sslKeys_t *keys, sslCertCb_t certCb, sslSessOpts_t *options) { ssl_t *lssl; if (!ssl) { return PS_ARG_FAIL; } if (options == NULL) { return PS_ARG_FAIL; } /* Add SERVER_FLAGS to versionFlag member of options */ options->versionFlag |= SSL_FLAGS_SERVER; *ssl = NULL; lssl = NULL; #ifdef USE_CLIENT_AUTH if (certCb) { options->versionFlag |= SSL_FLAGS_CLIENT_AUTH; if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) { goto NEW_SVR_ERROR; } matrixSslSetCertValidator(lssl, (sslCertCb_t)certCb); } else if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) { goto NEW_SVR_ERROR; } #else psAssert(certCb == NULL); if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) { goto NEW_SVR_ERROR; } #endif /* USE_CLIENT_AUTH */ lssl->userPtr = options->userPtr; if (options->maxFragLen < 0) { /* User wants to deny a client request for changing max frag len */ lssl->extFlags.deny_max_fragment_len = 1; } lssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN; if (options->truncHmac < 0) { lssl->extFlags.deny_truncated_hmac = 1; } /* Extended master secret is enabled by default. If user sets to 1 this is a flag to REQUIRE its use */ if (options->extendedMasterSecret > 0) { lssl->extFlags.require_extended_master_secret = 1; } *ssl = lssl; return MATRIXSSL_SUCCESS; NEW_SVR_ERROR: if (lssl) matrixSslDeleteSession(lssl); return PS_FAILURE; }
/* Client side. Make a socket connection and go through the SSL handshake phase in blocking mode. The last parameter is an optional function callback for user-level certificate validation. NULL if not needed. */ int sslConnect(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, sslSessionId_t *id, short cipherSuite, int (*certValidator)(sslCertInfo_t *t, void *arg)) { sslConn_t *conn; /* Create a new SSL session for the new socket and register the user certificate validator */ conn = calloc(sizeof(sslConn_t), 1); conn->fd = fd; if (matrixSslNewSession(&conn->ssl, keys, id, 0) < 0) { sslFreeConnection(&conn); return -1; } matrixSslSetCertValidator(conn->ssl, certValidator, NULL); *cpp = sslDoHandshake(conn, cipherSuite); if (*cpp == NULL) { return -1; } return 0; }
int sslConnect(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, sslSessionId_t *id, short cipherSuite, int (*certValidator)(sslCertInfo_t *t, void *arg)) { sslConn_t *conn; conn = calloc(sizeof(sslConn_t), 1); conn->fd = fd; if (matrixSslNewSession(&conn->ssl, keys, id, 0) < 0) { fprintf(stderr, "error %s:%d\n",__FILE__,__LINE__); sslFreeConnection(&conn); return -1; } matrixSslSetCertValidator(conn->ssl, certValidator, keys); *cpp = sslDoHandshake(conn, cipherSuite); if (*cpp == NULL) { fprintf(stderr, "error %s:%d\n",__FILE__,__LINE__); return -1; } return 0; }
SSL * SSL_new(sslKeys_t *keys, int flags) { SSL * ssl = (SSL *)calloc(1, sizeof(SSL)); if (!ssl) return 0; ssl->keys = keys; if ( matrixSslNewSession(&(ssl->ssl), ssl->keys, NULL, flags) < 0 ) {} ssl->insock.size = 1024; ssl->insock.buf = ssl->insock.start = ssl->insock.end = (unsigned char *)malloc(ssl->insock.size); ssl->outsock.size = 1024; ssl->outsock.buf = ssl->outsock.start = ssl->outsock.end = (unsigned char *)malloc(ssl->outsock.size); ssl->inbuf.size = 0; ssl->inbuf.buf = ssl->inbuf.start = ssl->inbuf.end = NULL; ssl->fd = -1; ssl->outBufferCount = 0; ssl->status = 0; ssl->pending = 1; return ssl; }
void matrixssl_new_session(int fp) { matrixssl_buf *pbuf = matrixssl_newbuf(fp); if (NULL == keys || NULL == pbuf || no_matrixssl_sessions >= MAX_MATRIXSSL_SESSIONS) return; matrixSslNewSession(&pbuf->ssl, keys, NULL, SSL_FLAGS_SERVER); }
/* Create a new server SSL session This creates internal SSL buffers and cipher structures Internal SSL state is set to expect an incoming 'HelloRequest' Return MATRIXSSL_SUCCESS on success < 0 on error */ int32 matrixSslNewServerSession(ssl_t **ssl, sslKeys_t *keys, int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert), int32 flags) { ssl_t *lssl; int32 lflags = SSL_FLAGS_SERVER; if (!ssl) { return PS_ARG_FAIL; } lflags |= flags; *ssl = NULL; lssl = NULL; #ifdef USE_CLIENT_AUTH if (certCb) { lflags |= SSL_FLAGS_CLIENT_AUTH; if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) { goto NEW_SVR_ERROR; } matrixSslSetCertValidator(lssl, (sslCertCb_t)certCb); } else if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) { goto NEW_SVR_ERROR; } #else psAssert(certCb == NULL); if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) { goto NEW_SVR_ERROR; } #endif /* USE_CLIENT_AUTH */ lssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN; *ssl = lssl; return MATRIXSSL_SUCCESS; NEW_SVR_ERROR: if (lssl) matrixSslDeleteSession(lssl); return PS_FAILURE; }
int sslAccept(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, int (*certValidator)(sslCertInfo_t *t, void *arg), int flags) { sslConn_t *conn; unsigned char buf[1024]; int status, rc; conn = calloc(sizeof(sslConn_t), 1); conn->fd = fd; if (matrixSslNewSession(&conn->ssl, keys, NULL, SSL_FLAGS_SERVER | flags) < 0) { sslFreeConnection(&conn); return -1; } #ifdef USE_CLIENT_AUTH matrixSslSetCertValidator(conn->ssl, certValidator, keys); #endif /* USE_CLIENT_AUTH */ memset(&conn->inbuf, 0x0, sizeof(sslBuf_t)); conn->insock.size = 1024; conn->insock.start = conn->insock.end = conn->insock.buf = (unsigned char *)malloc(conn->insock.size); conn->outsock.size = 1024; conn->outsock.start = conn->outsock.end = conn->outsock.buf = (unsigned char *)malloc(conn->outsock.size); conn->inbuf.size = 0; conn->inbuf.start = conn->inbuf.end = conn->inbuf.buf = NULL; *cpp = conn; readMore: rc = sslRead(conn, buf, sizeof(buf), &status); if (rc == 0) { if (status == SSLSOCKET_EOF || status == SSLSOCKET_CLOSE_NOTIFY) { sslFreeConnection(&conn); return -1; } if (matrixSslHandshakeIsComplete(conn->ssl) == 0) { goto readMore; } } else if (rc > 0) { socketAssert(0); return -1; } else { fprintf(stderr, "sslRead error in sslAccept\n"); sslFreeConnection(&conn); return -1; } *cpp = conn; return 0; }
/* Client side. Open a socket connection to a remote ip and port. This code is not specific to SSL. */ SOCKET socketConnect(char *ip, short port, int *err) { struct sockaddr_in addr; SOCKET fd; int rc; struct hostent *hent; char ipbuf[20]; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "Error creating socket\n"); *err = getSocketError(); return INVALID_SOCKET; } /* Make sure the socket is not inherited by exec'd processes Set the REUSEADDR flag to minimize the number of sockets in TIME_WAIT */ fcntl(fd, F_SETFD, FD_CLOEXEC); rc = 1; // setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); setSocketNodelay(fd); /* Turn on blocking mode for the connecting socket */ setSocketBlock(fd); /* //Marked by Gemtek hent = gethostbyname(ip); if (!hent) { fprintf(stderr, "Error resolving host\n"); } */ memset((char *) &addr, 0x0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); //Gemtek added sprintf( ipbuf ,"%s", "127.0.0.1" ); fprintf( stderr , "ip:port ==> %s:%d\n" , ipbuf , port ); //Gemtek added if( NULL != ip && strlen( ipbuf ) >= 7 && 0!=strcmp( ipbuf , "localhost") ) { //bcopy(hent->h_addr, &addr.sin_addr, hent->h_length); addr.sin_addr.s_addr = inet_addr( ipbuf ) ; } rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); #if WIN if (rc != 0) { #else if (rc < 0) { #endif *err = getSocketError(); return INVALID_SOCKET; } return fd; } /******************************************************************************/ /* Server side. Accept an incomming SSL connection request. 'conn' will be filled in with information about the accepted ssl connection return -1 on error, 0 on success, or WOULD_BLOCK for non-blocking sockets */ int sslAccept(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, int (*certValidator)(sslCertInfo_t *t, void *arg), int flags) { sslConn_t *conn; unsigned char buf[1024]; int status, rc; /* Associate a new ssl session with this socket. The session represents the state of the ssl protocol over this socket. Session caching is handled automatically by this api. */ conn = calloc(sizeof(sslConn_t), 1); conn->fd = fd; if (matrixSslNewSession(&conn->ssl, keys, NULL, SSL_FLAGS_SERVER | flags) < 0) { sslFreeConnection(&conn); return -1; } /* MatrixSSL doesn't provide buffers for data internally. Define them here to support buffered reading and writing for non-blocking sockets. Although it causes quite a bit more work, we support dynamically growing the buffers as needed. Alternately, we could define 16K buffers here and not worry about growing them. */ memset(&conn->inbuf, 0x0, sizeof(sslBuf_t)); conn->insock.size = 10240; conn->insock.start = conn->insock.end = conn->insock.buf = (unsigned char *)malloc(conn->insock.size); conn->outsock.size = 10240; conn->outsock.start = conn->outsock.end = conn->outsock.buf = (unsigned char *)malloc(conn->outsock.size); conn->inbuf.size = 0; conn->inbuf.start = conn->inbuf.end = conn->inbuf.buf = NULL; *cpp = conn; readMore: rc = sslRead(conn, buf, sizeof(buf), &status); /* Reading handshake records should always return 0 bytes, we aren't expecting any data yet. */ if (rc == 0) { if (status == SSLSOCKET_EOF || status == SSLSOCKET_CLOSE_NOTIFY) { sslFreeConnection(&conn); return -1; } if (matrixSslHandshakeIsComplete(conn->ssl) == 0) { goto readMore; } } else if (rc > 0) { socketAssert(0); return -1; } else { fprintf(stderr, "sslRead error in sslAccept\n"); sslFreeConnection(&conn); return -1; } *cpp = conn; return 0; }
int main(int argc, char **argv) { int opt; char *user =0; char *host; unsigned long port; int pid; int s; int conn; int delim; progname =*argv; phccmax =0; #ifdef SSLSVD while ((opt =getopt(argc, (const char **)argv, "c:C:i:x:u:l:Eb:hpt:vVU:/:Z:K:")) != opteof) { #else while ((opt =getopt(argc, (const char **)argv, "c:C:i:x:u:l:Eb:hpt:vV")) != opteof) { #endif switch(opt) { case 'c': scan_ulong(optarg, &cmax); if (cmax < 1) usage(); break; case 'C': delim =scan_ulong(optarg, &phccmax); if (phccmax < 1) usage(); if (optarg[delim] == ':') { if (ipsvd_fmt_msg(&msg, optarg +delim +1) == -1) die_nomem(); if (! stralloc_0(&msg)) die_nomem(); phccmsg =msg.s; } break; case 'i': if (instructs) usage(); instructs =optarg; break; case 'x': if (instructs) usage(); instructs =optarg; iscdb =1; break; case 'u': user =(char*)optarg; break; case 'l': if (! stralloc_copys(&local_hostname, optarg)) die_nomem(); if (! stralloc_0(&local_hostname)) die_nomem(); break; case 'E': ucspi =0; break; case 'b': scan_ulong(optarg, &backlog); break; case 'h': lookuphost =1; break; case 'p': lookuphost =1; paranoid =1; break; case 't': scan_ulong(optarg, &timeout); break; case 'v': ++verbose; break; #ifdef SSLSVD case 'U': ssluser =(char*)optarg; break; case '/': root =(char*)optarg; break; case 'Z': cert =(char*)optarg; break; case 'K': key =(char*)optarg; break; #endif case 'V': strerr_warn1(VERSION, 0); case '?': usage(); } } argv +=optind; if (! argv || ! *argv) usage(); host =*argv++; if (! argv || ! *argv) usage(); local_port =*argv++; if (! argv || ! *argv) usage(); prog =(const char **)argv; if (phccmax > cmax) phccmax =cmax; if (user) if (! uidgids_get(&ugid, user)) { if (errno) strerr_die4sys(111, FATAL, "unable to get user/group: ", user, ": "); strerr_die3x(100, FATAL, "unknown user/group: ", user); } #ifdef SSLSVD svuser =user; client =0; if ((getuid() == 0) && (! ssluser)) strerr_die2x(100, FATAL, "-U ssluser must be set when running as root"); if (ssluser) if (! uidgids_get(&sslugid, ssluser)) { if (errno) strerr_die4sys(111, FATAL, "unable to get user/group: ", ssluser, ": "); strerr_die3x(100, FATAL, "unknown user/group: ", ssluser); } if (! cert) cert ="./cert.pem"; if (! key) key =cert; if (matrixSslOpen() < 0) fatal("unable to initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("unable to read cert, key, or ca file"); fatal("unable to read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) strerr_die2x(111, FATAL, "unable to create ssl session"); #endif dns_random_init(seed); sig_block(sig_child); sig_catch(sig_child, sig_child_handler); sig_catch(sig_term, sig_term_handler); sig_ignore(sig_pipe); if (phccmax) if (ipsvd_phcc_init(cmax) == -1) die_nomem(); if (str_equal(host, "")) host ="0.0.0.0"; if (str_equal(host, "0")) host ="0.0.0.0"; if (! ipsvd_scan_port(local_port, "tcp", &port)) strerr_die3x(100, FATAL, "unknown port number or name: ", local_port); if (! stralloc_copys(&sa, host)) die_nomem(); if ((dns_ip4(&ips, &sa) == -1) || (ips.len < 4)) if (dns_ip4_qualify(&ips, &fqdn, &sa) == -1) fatal2("unable to look up ip address", host); if (ips.len < 4) strerr_die3x(100, FATAL, "unable to look up ip address: ", host); ips.len =4; if (! stralloc_0(&ips)) die_nomem(); local_ip[ipsvd_fmt_ip(local_ip, ips.s)] =0; if (! lookuphost) { if (! stralloc_copys(&remote_hostname, "")) die_nomem(); if (! stralloc_0(&remote_hostname)) die_nomem(); } if ((s =socket_tcp()) == -1) fatal("unable to create socket"); if (socket_bind4_reuse(s, ips.s, port) == -1) fatal("unable to bind socket"); if (listen(s, backlog) == -1) fatal("unable to listen"); ndelay_off(s); #ifdef SSLSVD #else if (user) { /* drop permissions */ if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to set groups"); if (setgid(*ugid.gid) == -1) fatal("unable to set gid"); if (prot_uid(ugid.uid) == -1) fatal("unable to set uid"); } #endif close(0); if (verbose) { out(INFO); out("listening on "); outfix(local_ip); out(":"); outfix(local_port); #ifdef SSLSVD #else if (user) { bufnum[fmt_ulong(bufnum, (unsigned long)ugid.uid)] =0; out(", uid "); out(bufnum); bufnum[fmt_ulong(bufnum, (unsigned long)ugid.gid)] =0; out(", gid "); out(bufnum); } #endif flush(", starting.\n"); } for (;;) { while (cnum >= cmax) sig_pause(); socka_size =sizeof(socka); sig_unblock(sig_child); conn =accept(s, (struct sockaddr *)&socka, &socka_size); sig_block(sig_child); if (conn == -1) { if (errno != error_intr) warn("unable to accept connection"); continue; } cnum++; if (verbose) connection_status(); if (phccmax) phcc =ipsvd_phcc_add((char*)&socka.sin_addr); if ((pid =fork()) == -1) { warn2("drop connection", "unable to fork"); close(conn); continue; } if (pid == 0) { /* child */ close(s); #ifdef SSLSVD if (*progname) *progname ='\\'; #endif connection_accept(conn); } if (phccmax) ipsvd_phcc_setpid(pid); close(conn); } _exit(0); }
int32_t matrixSslNewClientSession(ssl_t **ssl, const sslKeys_t *keys, sslSessionId_t *sid, const uint16_t cipherSpec[], uint8_t cipherSpecLen, sslCertCb_t certCb, const char *expectedName, tlsExtension_t *extensions, sslExtCb_t extCb, sslSessOpts_t *options) { ssl_t *lssl; psBuf_t tmp; uint32 len; int32 rc, i; if (!ssl) { return PS_ARG_FAIL; } if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) { return PS_ARG_FAIL; } if (options == NULL) { return PS_ARG_FAIL; } *ssl = NULL; lssl = NULL; /* Give priority to cipher suite if session id is provided and doesn't match */ if (cipherSpec != NULL && cipherSpec[0] != 0 && sid != NULL && sid->cipherId != 0) { rc = 1; for (i = 0; i < cipherSpecLen; i++) { if (cipherSpec[i] == sid->cipherId) { rc = 0; } } if (rc) { psTraceInfo("Explicit cipher suite will override session cache\n"); memset(sid->id, 0, SSL_MAX_SESSION_ID_SIZE); memset(sid->masterSecret, 0, SSL_HS_MASTER_SIZE); sid->cipherId = 0; } } if ((rc = matrixSslNewSession(&lssl, keys, sid, options)) < 0) { return rc; } lssl->userPtr = options->userPtr; #ifndef USE_ONLY_PSK_CIPHER_SUITE if (expectedName) { if (psX509ValidateGeneralName((char*)expectedName) < 0) { matrixSslDeleteSession(lssl); return rc; } rc = strlen(expectedName); lssl->expectedName = psMalloc(lssl->sPool, rc + 1); strcpy(lssl->expectedName, expectedName); } if (certCb) { matrixSslSetCertValidator(lssl, certCb); } #endif if (extCb) { lssl->extCb = extCb; } RETRY_HELLO: tmp.size = lssl->outsize; tmp.buf = tmp.start = tmp.end = lssl->outbuf; if ((rc = matrixSslEncodeClientHello(lssl, &tmp, cipherSpec, cipherSpecLen, &len, extensions, options)) < 0) { if (rc == SSL_FULL) { if ((tmp.buf = psRealloc(lssl->outbuf, len, lssl->bufferPool)) == NULL) { matrixSslDeleteSession(lssl); return PS_MEM_FAIL; } lssl->outbuf = tmp.buf; lssl->outsize = len; goto RETRY_HELLO; } else { matrixSslDeleteSession(lssl); return rc; } } psAssert(tmp.start == tmp.buf); lssl->outlen = tmp.end - tmp.start; *ssl = lssl; return MATRIXSSL_REQUEST_SEND; }
int tcpudpsvd_main(int argc ATTRIBUTE_UNUSED, char **argv) { char *str_C, *str_t; char *user; struct hcc *hccp; const char *instructs; char *msg_per_host = NULL; unsigned len_per_host = len_per_host; /* gcc */ #ifndef SSLSVD struct bb_uidgid_t ugid; #endif bool tcp; uint16_t local_port; char *preset_local_hostname = NULL; char *remote_hostname = remote_hostname; /* for compiler */ char *remote_addr = remote_addr; /* for compiler */ len_and_sockaddr *lsa; len_and_sockaddr local, remote; socklen_t sa_len; int pid; int sock; int conn; unsigned backlog = 20; INIT_G(); tcp = (applet_name[0] == 't'); /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */ opt_complementary = "-3:i--i:ph:vv:b+:c+"; #ifdef SSLSVD getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose ); #else /* "+": stop on first non-option */ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &verbose ); #endif if (option_mask32 & OPT_C) { /* -C n[:message] */ max_per_host = bb_strtou(str_C, &str_C, 10); if (str_C[0]) { if (str_C[0] != ':') bb_show_usage(); msg_per_host = str_C + 1; len_per_host = strlen(msg_per_host); } } if (max_per_host > cmax) max_per_host = cmax; if (option_mask32 & OPT_u) { if (!get_uidgid(&ugid, user, 1)) bb_error_msg_and_die("unknown user/group: %s", user); } #ifdef SSLSVD if (option_mask32 & OPT_U) ssluser = optarg; if (option_mask32 & OPT_slash) root = optarg; if (option_mask32 & OPT_Z) cert = optarg; if (option_mask32 & OPT_K) key = optarg; #endif argv += optind; if (!argv[0][0] || LONE_CHAR(argv[0], '0')) argv[0] = (char*)"0.0.0.0"; /* Per-IP flood protection is not thought-out for UDP */ if (!tcp) max_per_host = 0; bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ #ifdef SSLSVD sslser = user; client = 0; if ((getuid() == 0) && !(option_mask32 & OPT_u)) { xfunc_exitcode = 100; bb_error_msg_and_die("-U ssluser must be set when running as root"); } if (option_mask32 & OPT_u) if (!uidgid_get(&sslugid, ssluser, 1)) { if (errno) { bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); } bb_error_msg_and_die("unknown user/group '%s'", ssluser); } if (!cert) cert = "./cert.pem"; if (!key) key = cert; if (matrixSslOpen() < 0) fatal("cannot initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("cannot read cert, key, or ca file"); fatal("cannot read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) fatal("cannot create ssl session"); #endif sig_block(SIGCHLD); signal(SIGCHLD, sig_child_handler); bb_signals(BB_FATAL_SIGS, sig_term_handler); signal(SIGPIPE, SIG_IGN); if (max_per_host) ipsvd_perhost_init(cmax); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); lsa = xhost2sockaddr(argv[0], local_port); argv += 2; sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); sa_len = lsa->len; /* I presume sockaddr len stays the same */ xbind(sock, &lsa->u.sa, sa_len); if (tcp) xlisten(sock, backlog); else /* udp: needed for recv_from_to to work: */ socket_want_pktinfo(sock); /* ndelay_off(sock); - it is the default I think? */ #ifndef SSLSVD if (option_mask32 & OPT_u) { /* drop permissions */ xsetgid(ugid.gid); xsetuid(ugid.uid); } #endif if (verbose) { char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); bb_error_msg("listening on %s, starting", addr); free(addr); #ifndef SSLSVD if (option_mask32 & OPT_u) printf(", uid %u, gid %u", (unsigned)ugid.uid, (unsigned)ugid.gid); #endif } /* Main accept() loop */ again: hccp = NULL; while (cnum >= cmax) wait_for_any_sig(); /* expecting SIGCHLD */ /* Accept a connection to fd #0 */ again1: close(0); again2: sig_unblock(SIGCHLD); local.len = remote.len = sa_len; if (tcp) { conn = accept(sock, &remote.u.sa, &remote.len); } else { /* In case recv_from_to won't be able to recover local addr. * Also sets port - recv_from_to is unable to do it. */ local = *lsa; conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); } sig_block(SIGCHLD); if (conn < 0) { if (errno != EINTR) bb_perror_msg(tcp ? "accept" : "recv"); goto again2; } xmove_fd(tcp ? conn : sock, 0); if (max_per_host) { /* Drop connection immediately if cur_per_host > max_per_host * (minimizing load under SYN flood) */ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); if (cur_per_host > max_per_host) { /* ipsvd_perhost_add detected that max is exceeded * (and did not store ip in connection table) */ free(remote_addr); if (msg_per_host) { /* don't block or test for errors */ send(0, msg_per_host, len_per_host, MSG_DONTWAIT); } goto again1; } /* NB: remote_addr is not leaked, it is stored in conn table */ } if (!tcp) { /* Voodoo magic: making udp sockets each receive its own * packets is not trivial, and I still not sure * I do it 100% right. * 1) we have to do it before fork() * 2) order is important - is it right now? */ /* Open new non-connected UDP socket for further clients... */ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); /* Make plain write/send work for old socket by supplying default * destination address. This also restricts incoming packets * to ones coming from this remote IP. */ xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? * Yup, time window is very small, but it exists (is it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); /* Doesn't work: * we cannot replace fd #0 - we will lose pending packet * which is already buffered for us! And we cannot use fd #1 * instead - it will "intercept" all following packets, but child * does not expect data coming *from fd #1*! */ #if 0 /* Make it so that local addr is fixed to localp->u.sa * and we don't accidentally accept packets to other local IPs. */ /* NB: we possibly bind to the _very_ same_ address & port as the one * already bound in parent! This seems to work in Linux. * (otherwise we can move socket to fd #0 only if bind succeeds) */ close(0); set_nport(localp, htons(local_port)); xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); setsockopt_reuseaddr(0); /* crucial */ xbind(0, &localp->u.sa, localp->len); #endif } pid = vfork(); if (pid == -1) { bb_perror_msg("vfork"); goto again; } if (pid != 0) { /* Parent */ cnum++; if (verbose) connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ undo_xsetenv(); goto again; } /* Child: prepare env, log, and exec prog */ /* Closing tcp listening socket */ if (tcp) close(sock); { /* vfork alert! every xmalloc in this block should be freed! */ char *local_hostname = local_hostname; /* for compiler */ char *local_addr = NULL; char *free_me0 = NULL; char *free_me1 = NULL; char *free_me2 = NULL; if (verbose || !(option_mask32 & OPT_E)) { if (!max_per_host) /* remote_addr is not yet known */ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); if (option_mask32 & OPT_h) { free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); if (!remote_hostname) { bb_error_msg("cannot look up hostname for %s", remote_addr); remote_hostname = remote_addr; } } /* Find out local IP peer connected to. * Errors ignored (I'm not paranoid enough to imagine kernel * which doesn't know local IP). */ if (tcp) getsockname(0, &local.u.sa, &local.len); /* else: for UDP it is done earlier by parent */ local_addr = xmalloc_sockaddr2dotted(&local.u.sa); if (option_mask32 & OPT_h) { local_hostname = preset_local_hostname; if (!local_hostname) { free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); if (!local_hostname) bb_error_msg_and_die("cannot look up hostname for %s", local_addr); } /* else: local_hostname is not NULL, but is NOT malloced! */ } } if (verbose) { pid = getpid(); if (max_per_host) { bb_error_msg("concurrency %s %u/%u", remote_addr, cur_per_host, max_per_host); } bb_error_msg((option_mask32 & OPT_h) ? "start %u %s-%s (%s-%s)" : "start %u %s-%s", pid, local_addr, remote_addr, local_hostname, remote_hostname); } if (!(option_mask32 & OPT_E)) { /* setup ucspi env */ const char *proto = tcp ? "TCP" : "UDP"; /* Extract "original" destination addr:port * from Linux firewall. Useful when you redirect * an outbond connection to local handler, and it needs * to know where it originally tried to connect */ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { char *addr = xmalloc_sockaddr2dotted(&local.u.sa); xsetenv_plain("TCPORIGDSTADDR", addr); free(addr); } xsetenv_plain("PROTO", proto); xsetenv_proto(proto, "LOCALADDR", local_addr); xsetenv_proto(proto, "REMOTEADDR", remote_addr); if (option_mask32 & OPT_h) { xsetenv_proto(proto, "LOCALHOST", local_hostname); xsetenv_proto(proto, "REMOTEHOST", remote_hostname); } //compat? xsetenv_proto(proto, "REMOTEINFO", ""); /* additional */ if (cur_per_host > 0) /* can not be true for udp */ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); } free(local_addr); free(free_me0); free(free_me1); free(free_me2); } xdup2(0, 1); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGCHLD, SIG_DFL); sig_unblock(SIGCHLD); #ifdef SSLSVD strcpy(id, utoa(pid)); ssl_io(0, argv); #else BB_EXECVP(argv[0], argv); #endif bb_perror_msg_and_die("exec '%s'", argv[0]); }
int ssl_io(unsigned int newsession, const char **prog) { if (client) { fdstdin =6; fdstdou =7; } bad_certificate = env_get("SSLIO_BAD_CERTIFICATE"); if ((s =env_get("SSLIO_BUFIN"))) scan_ulong(s, &bufsizein); if ((s =env_get("SSLIO_BUFOU"))) scan_ulong(s, &bufsizeou); if (bufsizein < 64) bufsizein =64; if (bufsizeou < 64) bufsizeou =64; if ((s =env_get("SSLIO_HANDSHAKE_TIMEOUT"))) scan_ulong(s, &handshake_timeout); if (handshake_timeout < 1) handshake_timeout =1; if (pipe(encpipe) == -1) fatalm("unable to create pipe for encoding"); if (pipe(decpipe) == -1) fatalm("unable to create pipe for decoding"); if ((pid =fork()) == -1) fatalm("unable to fork"); if (pid == 0) { if (close(encpipe[1]) == -1) fatalm("unable to close encoding pipe output"); if (close(decpipe[0]) == -1) fatalm("unable to close decoding pipe input"); if (newsession) if (matrixSslOpen() < 0) fatalm("unable to initialize ssl"); if (root) { if (chdir(root) == -1) fatalm("unable to change to new root directory"); if (chroot(".") == -1) fatalm("unable to chroot"); } if (ssluser) { /* drop permissions */ if (setgroups(sslugid.gids, sslugid.gid) == -1) fatal("unable to set groups"); if (setgid(*sslugid.gid) == -1) fatal("unable to set gid"); if (prot_uid(sslugid.uid) == -1) fatalm("unable to set uid"); } if (newsession) { if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatalm("unable to read cert, key, or ca file"); fatalm("unable to read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, client?0:SSL_FLAGS_SERVER) < 0) fatalmx("unable to create ssl session"); } if (client) if (ca || bad_certificate) matrixSslSetCertValidator(ssl, &validate, 0); sig_catch(sig_term, sig_term_handler); sig_ignore(sig_pipe); doio(); finish(); _exit(0); } if (close(encpipe[0]) == -1) fatalm("unable to close encoding pipe input"); if (close(decpipe[1]) == -1) fatalm("unable to close decoding pipe output"); if (fd_move(fdstdin, decpipe[0]) == -1) fatalm("unable to setup filedescriptor for decoding"); if (fd_move(fdstdou, encpipe[1]) == -1) fatalm("unable to setup filedescriptor for encoding"); sslCloseOsdep(); if (svuser) { if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to set groups for prog"); if (setgid(*ugid.gid) == -1) fatal("unable to set gid for prog"); if (prot_uid(ugid.uid) == -1) fatalm("unable to set uid for prog"); } pathexec(prog); fatalm("unable to run prog"); return(111); }