コード例 #1
0
ファイル: ftp-proxy.c プロジェクト: SylvestreG/bitrig
void
end_session(struct session *s)
{
	int err;

	logmsg(LOG_INFO, "#%d ending session", s->id);

	/* Flush output buffers. */
	if (s->client_bufev && s->client_fd != -1)
		evbuffer_write(s->client_bufev->output, s->client_fd);
	if (s->server_bufev && s->server_fd != -1)
		evbuffer_write(s->server_bufev->output, s->server_fd);

	if (s->client_fd != -1)
		close(s->client_fd);
	if (s->server_fd != -1)
		close(s->server_fd);

	if (s->client_bufev)
		bufferevent_free(s->client_bufev);
	if (s->server_bufev)
		bufferevent_free(s->server_bufev);

	/* Remove rulesets by commiting empty ones. */
	err = 0;
	if (prepare_commit(s->id) == -1)
		err = errno;
	else if (do_commit() == -1) {
		err = errno;
		do_rollback();
	}
	if (err)
		logmsg(LOG_ERR, "#%d pf rule removal failed: %s", s->id,
		    strerror(err));

	LIST_REMOVE(s, entry);
	free(s);
	session_count--;
}
コード例 #2
0
ファイル: tftp-proxy.c プロジェクト: IIJ-NetBSD/netbsd-src
int
main(int argc, char *argv[])
{
	int c, fd = 0, on = 1, out_fd = 0, peer, reqsize = 0;
	int transwait = DEFTRANSWAIT;
	char *p;
	struct tftphdr *tp;
	struct passwd *pw;
	size_t cbuflen;
	char *cbuf;
	char req[PKTSIZE];
	struct cmsghdr *cmsg;
	struct msghdr msg;
	struct iovec iov;

	struct sockaddr_storage from, proxy, server, proxy_to_server, s_in;
	struct sockaddr_in sock_out;
	socklen_t j;
	in_port_t bindport;

	openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);

	while ((c = getopt(argc, argv, "vw:")) != -1)
		switch (c) {
		case 'v':
			verbose++;
			break;
		case 'w':
			transwait = strtoll(optarg, &p, 10);
			if (transwait < 1) {
				syslog(LOG_ERR, "invalid -w value");
				exit(1);
			}
			break;
		default:
			usage();
			break;
		}

	/* open /dev/pf */
	init_filter(NULL, verbose);

	tzset();

	pw = getpwnam(NOPRIV_USER);
	if (!pw) {
		syslog(LOG_ERR, "no such user %s: %m", NOPRIV_USER);
		exit(1);
	}
	if (chroot(CHROOT_DIR) || chdir("/")) {
		syslog(LOG_ERR, "chroot %s: %m", CHROOT_DIR);
		exit(1);
	}
#ifdef __NetBSD__
	if (setgroups(1, &pw->pw_gid) ||
	    setgid(pw->pw_gid) ||
	    setuid(pw->pw_uid)) {
		syslog(LOG_ERR, "can't revoke privs: %m");
		exit(1);
	}
#else
	if (setgroups(1, &pw->pw_gid) ||
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
		syslog(LOG_ERR, "can't revoke privs: %m");
		exit(1);
	}
#endif /* !__NetBSD__ */

	/* non-blocking io */
	if (ioctl(fd, FIONBIO, &on) < 0) {
		syslog(LOG_ERR, "ioctl(FIONBIO): %m");
		exit(1);
	}

	if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) {
		syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
		exit(1);
	}

	j = sizeof(s_in);
	if (getsockname(fd, (struct sockaddr *)&s_in, &j) == -1) {
		syslog(LOG_ERR, "getsockname: %m");
		exit(1);
	}

	bindport = ((struct sockaddr_in *)&s_in)->sin_port;

	/* req will be pushed back out at the end, unchanged */
	j = sizeof(from);
	if ((reqsize = recvfrom(fd, req, sizeof(req), MSG_PEEK,
	    (struct sockaddr *)&from, &j)) < 0) {
		syslog(LOG_ERR, "recvfrom: %m");
		exit(1);
	}

	bzero(&msg, sizeof(msg));
	iov.iov_base = req;
	iov.iov_len = sizeof(req);
	msg.msg_name = &from;
	msg.msg_namelen = sizeof(from);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	cbuflen = CMSG_SPACE(sizeof(struct sockaddr_storage));
	if ((cbuf = malloc(cbuflen)) == NULL) {
		syslog(LOG_ERR, "malloc: %m");
		exit(1);
	}

	msg.msg_control = cbuf;
	msg.msg_controllen = cbuflen;

	if (recvmsg(fd, &msg, 0) < 0) {
		syslog(LOG_ERR, "recvmsg: %m");
		exit(1);
	}

	close(fd);
	close(1);

	peer = socket(from.ss_family, SOCK_DGRAM, 0);
	if (peer < 0) {
		syslog(LOG_ERR, "socket: %m");
		exit(1);
	}
	memset(&s_in, 0, sizeof(s_in));
	s_in.ss_family = from.ss_family;
	s_in.ss_len = from.ss_len;

	/* get local address if possible */
	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
		if (cmsg->cmsg_level == IPPROTO_IP &&
		    cmsg->cmsg_type == IP_RECVDSTADDR) {
			memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
			    CMSG_DATA(cmsg), sizeof(struct in_addr));
			break;
		}
	}

	if (bind(peer, (struct sockaddr *)&s_in, s_in.ss_len) < 0) {
		syslog(LOG_ERR, "bind: %m");
		exit(1);
	}
	if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
		syslog(LOG_ERR, "connect: %m");
		exit(1);
	}

	tp = (struct tftphdr *)req;
	if (!(ntohs(tp->th_opcode) == RRQ || ntohs(tp->th_opcode) == WRQ)) {
		/* not a tftp request, bail */
		if (verbose) {
			syslog(LOG_WARNING, "not a valid tftp request");
			exit(1);
		} else
			/* exit 0 so inetd doesn't log anything */
			exit(0);
	}

	j = sizeof(struct sockaddr_storage);
	if (getsockname(fd, (struct sockaddr *)&proxy, &j) == -1) {
		syslog(LOG_ERR, "getsockname: %m");
		exit(1);
	}

	((struct sockaddr_in *)&proxy)->sin_port = bindport;

	/* find the un-rdr'd server and port the client wanted */
	if (server_lookup((struct sockaddr *)&from,
	    (struct sockaddr *)&proxy, (struct sockaddr *)&server,
	    IPPROTO_UDP) != 0) {
		syslog(LOG_ERR, "pf connection lookup failed (no rdr?)");
		exit(1);
	}

	/* establish a new outbound connection to the remote server */
	if ((out_fd = socket(((struct sockaddr *)&from)->sa_family,
	    SOCK_DGRAM, IPPROTO_UDP)) < 0) {
		syslog(LOG_ERR, "couldn't create new socket");
		exit(1);
	}

	bzero((char *)&sock_out, sizeof(sock_out));
	sock_out.sin_family = from.ss_family;
	sock_out.sin_port = htons(pick_proxy_port());
	if (bind(out_fd, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
		syslog(LOG_ERR, "couldn't bind to new socket: %m");
		exit(1);
	}

	if (connect(out_fd, (struct sockaddr *)&server,
	    ((struct sockaddr *)&server)->sa_len) < 0 && errno != EINPROGRESS) {
		syslog(LOG_ERR, "couldn't connect to remote server: %m");
		exit(1);
	}

	j = sizeof(struct sockaddr_storage);
	if ((getsockname(out_fd, (struct sockaddr *)&proxy_to_server,
	    &j)) < 0) {
		syslog(LOG_ERR, "getsockname: %m");
		exit(1);
	}

	if (verbose)
		syslog(LOG_INFO, "%s:%d -> %s:%d/%s:%d -> %s:%d \"%s %s\"",
			sock_ntop((struct sockaddr *)&from),
			ntohs(((struct sockaddr_in *)&from)->sin_port),
			sock_ntop((struct sockaddr *)&proxy),
			ntohs(((struct sockaddr_in *)&proxy)->sin_port),
			sock_ntop((struct sockaddr *)&proxy_to_server),
			ntohs(((struct sockaddr_in *)&proxy_to_server)->sin_port),
			sock_ntop((struct sockaddr *)&server),
			ntohs(((struct sockaddr_in *)&server)->sin_port),
			opcode(ntohs(tp->th_opcode)),
			tp->th_stuff);

	/* get ready to add rdr and pass rules */
	if (prepare_commit(1) == -1) {
		syslog(LOG_ERR, "couldn't prepare pf commit");
		exit(1);
	}

	/* rdr from server to us on our random port -> client on its port */
	if (add_rdr(1, (struct sockaddr *)&server,
	    (struct sockaddr *)&proxy_to_server, ntohs(sock_out.sin_port),
	    (struct sockaddr *)&from,
	    ntohs(((struct sockaddr_in *)&from)->sin_port),
	    IPPROTO_UDP) == -1) {
		syslog(LOG_ERR, "couldn't add rdr");
		exit(1);
	}

	/* explicitly allow the packets to return back to the client (which pf
	 * will see post-rdr) */
	if (add_filter(1, PF_IN, (struct sockaddr *)&server,
	    (struct sockaddr *)&from,
	    ntohs(((struct sockaddr_in *)&from)->sin_port),
	    IPPROTO_UDP) == -1) {
		syslog(LOG_ERR, "couldn't add pass in");
		exit(1);
	}
	if (add_filter(1, PF_OUT, (struct sockaddr *)&server,
	    (struct sockaddr *)&from,
	    ntohs(((struct sockaddr_in *)&from)->sin_port),
	    IPPROTO_UDP) == -1) {
		syslog(LOG_ERR, "couldn't add pass out");
		exit(1);
	}

	/* and just in case, to pass out from us to the server */
	if (add_filter(1, PF_OUT, (struct sockaddr *)&proxy_to_server,
	    (struct sockaddr *)&server,
	    ntohs(((struct sockaddr_in *)&server)->sin_port),
	    IPPROTO_UDP) == -1) {
		syslog(LOG_ERR, "couldn't add pass out");
		exit(1);
	}

	if (do_commit() == -1) {
		syslog(LOG_ERR, "couldn't commit pf rules");
		exit(1);
	}

	/* forward the initial tftp request and start the insanity */
	if (send(out_fd, tp, reqsize, 0) < 0) {
		syslog(LOG_ERR, "couldn't forward tftp packet: %m");
		exit(1);
	}

	/* allow the transfer to start to establish a state */
	sleep(transwait);

	/* delete our rdr rule and clean up */
	prepare_commit(1);
	do_commit();

	return(0);
}
コード例 #3
0
ファイル: ftp-proxy.c プロジェクト: SylvestreG/bitrig
int
allow_data_connection(struct session *s)
{
	struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa;
	int prepared = 0;

	/*
	 * The pf rules below do quite some NAT rewriting, to keep up
	 * appearances.  Points to keep in mind:
	 * 1)  The client must think it's talking to the real server,
	 *     for both control and data connections.  Transparently.
	 * 2)  The server must think that the proxy is the client.
	 * 3)  Source and destination ports are rewritten to minimize
	 *     port collisions, to aid security (some systems pick weak
	 *     ports) or to satisfy RFC requirements (source port 20).
	 */
	
	/* Cast this once, to make code below it more readable. */
	client_sa = sstosa(&s->client_ss);
	server_sa = sstosa(&s->server_ss);
	proxy_sa = sstosa(&s->proxy_ss);
	if (fixed_server)
		/* Fixed server: data connections must appear to come
		   from / go to the original server, not the fixed one. */
		orig_sa = sstosa(&s->orig_server_ss);
	else
		/* Server not fixed: orig_server == server. */
		orig_sa = sstosa(&s->server_ss);

	/* Passive modes. */
	if (s->cmd == CMD_PASV || s->cmd == CMD_EPSV) {
		s->port = parse_port(s->cmd);
		if (s->port < MIN_PORT) {
			logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id,
			    linebuf);
			return (0);
		}
		s->proxy_port = pick_proxy_port();
		logmsg(LOG_INFO, "#%d passive: client to server port %d"
		    " via port %d", s->id, s->port, s->proxy_port);

		if (prepare_commit(s->id) == -1)
			goto fail;
		prepared = 1;

		proxy_reply(s->cmd, orig_sa, s->proxy_port);
		logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf);

		/* pass in from $client to $orig_server port $proxy_port
		    rdr-to $server port $port */
		if (add_rdr(s->id, client_sa, s->client_rd, orig_sa,
		    s->proxy_port, server_sa, s->port, getrtable()) == -1)
			goto fail;

		/* pass out from $client to $server port $port nat-to $proxy */
		if (add_nat(s->id, client_sa, getrtable(), server_sa,
		    s->port, proxy_sa, PF_NAT_PROXY_PORT_LOW,
		    PF_NAT_PROXY_PORT_HIGH) == -1)
			goto fail;
	}

	/* Active modes. */
	if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) {
		logmsg(LOG_INFO, "#%d active: server to client port %d"
		    " via port %d", s->id, s->port, s->proxy_port);

		if (prepare_commit(s->id) == -1)
			goto fail;
		prepared = 1;

		/* pass in from $server to $proxy port $proxy_port
		    rdr-to $client port $port */
		if (add_rdr(s->id, server_sa, getrtable(), proxy_sa,
		    s->proxy_port, client_sa, s->port, s->client_rd) == -1)
			goto fail;

		/* pass out from $server to $client port $port
		    nat-to $orig_server port $natport */
		if (rfc_mode && s->cmd == CMD_PORT) {
			/* Rewrite sourceport to RFC mandated 20. */
			if (add_nat(s->id, server_sa, s->client_rd, client_sa,
			    s->port, orig_sa, 20, 20) == -1)
				goto fail;
		} else {
			/* Let pf pick a source port from the standard range. */
			if (add_nat(s->id, server_sa, s->client_rd, client_sa,
			    s->port, orig_sa, PF_NAT_PROXY_PORT_LOW,
			    PF_NAT_PROXY_PORT_HIGH) == -1)
			    	goto fail;
		}
	}

	/* Commit rules if they were prepared. */
	if (prepared && (do_commit() == -1)) {
		if (errno != EBUSY)
			goto fail;
		/* One more try if busy. */
		usleep(5000);
		if (do_commit() == -1)
			goto fail;
	}

	s->cmd = CMD_NONE;
	s->port = 0;

	return (1);

 fail:
	logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno));
	if (prepared)
		do_rollback();
	return (0);
}