Initial commit - fingered - Fingerd protocol daemon, allowing custom responses.
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 35e0510a8c69994716ba590b5d697f790f50e6f1
(HTM) Author: Jay Scott <me@jay.scot>
Date: Thu, 24 Aug 2023 14:49:36 +0100
Initial commit
Diffstat:
A .build.yml | 32 +++++++++++++++++++++++++++++++
A .gitignore | 1 +
A LICENSE | 15 +++++++++++++++
A Makefile | 66 +++++++++++++++++++++++++++++++
A README.md | 89 +++++++++++++++++++++++++++++++
A config/config.go | 67 +++++++++++++++++++++++++++++++
A example/default | 14 ++++++++++++++
A example/info | 646 +++++++++++++++++++++++++++++++
A example/jimmy | 110 +++++++++++++++++++++++++++++++
A example/uptime | 8 ++++++++
A go.mod | 3 +++
A main.go | 95 ++++++++++++++++++++++++++++++
A tests/utils_test.go | 113 +++++++++++++++++++++++++++++++
A utils/utils.go | 66 +++++++++++++++++++++++++++++++
14 files changed, 1325 insertions(+), 0 deletions(-)
---
(DIR) diff --git a/.build.yml b/.build.yml
@@ -0,0 +1,32 @@
+image: alpine/latest
+
+sources:
+ - https://git.sr.ht/~jayscott/fingered
+
+packages:
+ - go
+
+tasks:
+ - setup: |
+ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+
+ - format-check: |
+ cd fingered
+ unformatted=$(gofmt -l .)
+ if [[ -n "$unformatted" ]]; then
+ echo "The following files have formatting issues:"
+ echo "$unformatted"
+ exit 1
+ fi
+
+ - lint: |
+ cd fingered
+ ~/go/bin/golangci-lint run
+
+ - test: |
+ cd fingered
+ go test ./tests
+
+ - build: |
+ cd fingered
+ make build
(DIR) diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+bin/
(DIR) diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2023 Jay Scott
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
(DIR) diff --git a/Makefile b/Makefile
@@ -0,0 +1,66 @@
+# Makefile
+
+.PHONY: all fmt lint test build clean install
+
+BIN := bin
+NAME := fingered
+VER := 0.1.0
+
+BUILD_FLAGS := -ldflags="-s -w"
+
+# List of target platforms
+PLATFORMS := \
+ linux_amd64 \
+ linux_386 \
+ darwin_amd64 \
+ freebsd_amd64 \
+ freebsd_386 \
+ openbsd_amd64 \
+ openbsd_386
+
+# Generate binary paths
+BINS := $(addprefix $(BIN)/$(NAME)_$(VER)_,$(PLATFORMS))
+
+# Installation path
+INSTALL_PATH := /usr/local/bin
+
+# Detect current platform and architecture
+GOOS := $(shell go env GOOS)
+GOARCH := $(shell go env GOARCH)
+
+# Default target: format, lint, build
+all: fmt lint build
+
+fmt:
+ @echo "Formatting code..."
+ go fmt ./...
+
+lint:
+ @echo "Running linters..."
+ golangci-lint run
+
+test:
+ @echo "Running tests..."
+ go test ./tests
+
+# Build binaries for target platforms
+build: $(BINS)
+
+$(BIN)/$(NAME)_$(VER)_%: main.go
+ @echo "Building for $*..."
+ GOARCH=$(word 2,$(subst _, ,$*)) GOOS=$(word 1,$(subst _, ,$*)) go build ${BUILD_FLAGS} -o $@ main.go
+
+# Clean up artifacts
+clean:
+ go clean
+ rm -f $(BINS)
+
+# Install the binary for the current platform and architecture
+install: $(BIN)/$(NAME)_$(VER)_$(GOOS)_$(GOARCH)
+ @echo "Installing binary to $(INSTALL_PATH)..."
+ mkdir -p $(INSTALL_PATH)
+ cp -f $(BIN)/$(NAME)_$(VER)_$(GOOS)_$(GOARCH) $(INSTALL_PATH)/$(NAME)
+ chmod +x $(INSTALL_PATH)/$(NAME)
+
+run: build
+ ./$(BIN)/$(NAME)_$(VER)_linux_amd64
(DIR) diff --git a/README.md b/README.md
@@ -0,0 +1,89 @@
+# FINGERED
+
+[](https://builds.sr.ht/~jayscott/fingered/commits/master/.build.yml?)
+
+**A personal finger protocol daemon**
+
+A finger daemon that's all about giving you the finger — well, more like
+giving your queries the right answers. No fuss, no frills, just good ol'
+fashioned finger protocol fun.
+
+## Features
+
+- **Customize responses based on queried usernames**
+- **Script execution or text display responses**
+- **Default reply for unspecified queries.**
+
+
+## Installing
+
+From source (requires Go):
+```shell
+git clone https://git.sr.ht/~jayscott/fingered
+cd fingered
+make
+sudo make install
+```
+
+Pre-compiled binaries are available on the latest build artifacts:
+
+
+## Running
+
+From your terminal:
+```shell
+fingered
+```
+
+
+## Usage
+
+```shell
+Usage:
+ -d: Directory containing user files (default: /srv/fingered)
+ -f: Filename for empty requests (default: default)
+ -p: Port for incoming connections (default: 79)
+ -t: Number of worker threads (default: 10)
+```
+
+
+## Examples
+
+Run FINGERED on port 7000, using files from /srv/fingered/example, with
+'info' as the default file:
+
+```shell
+fingered -p 7000 -d /srv/fingered/example -f info
+```
+
+
+## Issue tracker
+
+For issues, [visit the tracker](https://todo.sr.ht/~jayscott/fingered).
+
+
+## Contributing
+
+Sending patches is done [by
+email](https://lists.sr.ht/~jayscott/fingered-dev), this is simple and
+built-in to Git.
+
+Set up your system once by following the steps Installation and
+Configuration of [git-send-email.io](https://git-send-email.io/)
+
+Then, run once in this repository:
+```shell
+git config sendemail.to "~jayscott/fingered-dev@lists.sr.ht"
+```
+
+Then, to send a patch, make your commit, then run:
+```shell
+git send-email --base=HEAD~1 --annotate -1 -v1
+```
+
+Your patch will appear on the [the mailing list](https://lists.sr.ht/~jayscott/fingered-dev/patches).
+
+
+## License
+
+This is open source! Please use it under the ISC License.
(DIR) diff --git a/config/config.go b/config/config.go
@@ -0,0 +1,67 @@
+package config
+
+import (
+ "flag"
+ "fmt"
+ "log"
+)
+
+const (
+ maxPort = 65535
+ minPort = 1
+ minThreads = 1
+)
+
+type Config struct {
+ Threads int
+ Port int
+ Dir string
+ Index string
+}
+
+func defaultConfig() Config {
+ return Config{
+ Threads: 10,
+ Port: 79,
+ Dir: "/srv/fingered",
+ Index: "default",
+ }
+}
+
+func displayUsage() {
+ flagSet := flag.CommandLine
+ flag.Usage = func() {
+ fmt.Printf("Usage:\n")
+ flagSet.VisitAll(func(flag *flag.Flag) {
+ fmt.Printf("\t-%s: %s (default: %s)\n", flag.Name, flag.Usage, flag.DefValue)
+ })
+ }
+}
+
+func addFlags(cfg *Config) {
+ flag.IntVar(&cfg.Threads, "t", cfg.Threads, "Number of worker threads")
+ flag.IntVar(&cfg.Port, "p", cfg.Port, "Port for incoming connections")
+ flag.StringVar(&cfg.Dir, "d", cfg.Dir, "Directory containing user files")
+ flag.StringVar(&cfg.Index, "f", cfg.Index, "Filename for empty requests")
+}
+
+func ParseFlags() Config {
+ cfg := defaultConfig()
+ addFlags(&cfg)
+ displayUsage()
+ flag.Parse()
+
+ if cfg.Threads < minThreads {
+ log.Fatal("Invalid number of threads: must be greater than 0.")
+ }
+
+ if cfg.Port < minPort || cfg.Port > maxPort {
+ log.Fatal("Invalid port value: must be between 1 and 65535.")
+ }
+
+ if cfg.Dir == "" {
+ log.Fatal("Invalid path value: directory cannot be empty.")
+ }
+
+ return cfg
+}
(DIR) diff --git a/example/default b/example/default
@@ -0,0 +1,14 @@
+ ___ __ __
+.' _|__|.-----.-----.-----.----.-----.--| |
+| _| || | _ | -__| _| -__| _ |
+|__| |__||__|__|___ |_____|__| |_____|_____|
+ |_____|
+
+
+Welcome to fingered!
+
+
+Available Fingers:
+
+ info ... get information about finger
+ jimmy ... we are all jimmy
(DIR) diff --git a/example/info b/example/info
@@ -0,0 +1,646 @@
+Network Working Group D. Zimmerman
+Request for Comments: 1288 Center for Discrete Mathematics and
+Obsoletes: RFCs 1196, 1194, 742 Theoretical Computer Science
+ December 1991
+
+
+ The Finger User Information Protocol
+
+Status of this Memo
+
+ This memo defines a protocol for the exchange of user information.
+ This RFC specifies an IAB standards track protocol for the Internet
+ community, and requests discussion and suggestions for improvements.
+ Please refer to the current edition of the "IAB Official Protocol
+ Standards" for the standardization state and status of this protocol.
+ Distribution of this memo is unlimited.
+
+Abstract
+
+ This memo describes the Finger user information protocol. This is a
+ simple protocol which provides an interface to a remote user
+ information program.
+
+ Based on RFC 742, a description of the original Finger protocol, this
+ memo attempts to clarify the expected communication between the two
+ ends of a Finger connection. It also tries not to invalidate the
+ many existing implementations or add unnecessary restrictions to the
+ original protocol definition.
+
+ This edition corrects and clarifies RFC 1196.
+
+ Table of Contents
+
+ 1. Introduction ........................................... 2
+ 1.1. Intent ............................................... 2
+ 1.2. History .............................................. 3
+ 1.3. Requirements ......................................... 3
+ 1.4. Updates .............................................. 3
+ 2. Use of the protocol .................................... 4
+ 2.1. Flow of events ....................................... 4
+ 2.2. Data format .......................................... 4
+ 2.3. Query specifications ................................. 4
+ 2.4. RUIP {Q2} behavior ................................... 5
+ 2.5. Expected RUIP response ............................... 6
+ 2.5.1. {C} query .......................................... 6
+ 2.5.2. {U}{C} query ....................................... 6
+ 2.5.3. {U} ambiguity ...................................... 7
+ 2.5.4. /W query token ..................................... 7
+
+
+
+Zimmerman [Page 1]
+
+RFC 1288 Finger December 1991
+
+
+ 2.5.5. Vending machines ................................... 7
+ 3. Security ............................................... 7
+ 3.1. Implementation security .............................. 7
+ 3.2. RUIP security ........................................ 8
+ 3.2.1. {Q2} refusal ....................................... 8
+ 3.2.2. {C} refusal ........................................ 8
+ 3.2.3. Atomic discharge ................................... 8
+ 3.2.4. User information files ............................. 9
+ 3.2.5. Execution of user programs ......................... 9
+ 3.2.6. {U} ambiguity ...................................... 9
+ 3.2.7. Audit trails ....................................... 9
+ 3.3. Client security ...................................... 9
+ 4. Examples ............................................... 10
+ 4.1. Example with a null command line ({C}) ............... 10
+ 4.2. Example with name specified ({U}{C}) ................. 10
+ 4.3. Example with ambiguous name specified ({U}{C}) ....... 11
+ 4.4. Example of query type {Q2} ({U}{H}{H}{C}) ............ 11
+ 5. Acknowledgments ........................................ 12
+ 6. Security Considerations ................................ 12
+ 7. Author's Address ....................................... 12
+
+1. Introduction
+
+1.1. Intent
+
+ This memo describes the Finger user information protocol. This is a
+ simple protocol which provides an interface to a remote user
+ information program (RUIP).
+
+ Based on RFC 742, a description of the original Finger protocol, this
+ memo attempts to clarify the expected communication between the two
+ ends of a Finger connection. It also tries not to invalidate the
+ many current implementations or add unnecessary restrictions to the
+ original protocol definition.
+
+ The most prevalent implementations of Finger today seem to be
+ primarily derived from the BSD UNIX work at the University of
+ California, Berkeley. Thus, this memo is based around the BSD
+ version's behavior.
+
+ However, the BSD version provides few options to tailor the Finger
+ RUIP for a particular site's security policy, or to protect the user
+ from dangerous data. Furthermore, there are MANY potential security
+ holes that implementors and administrators need to be aware of,
+ particularly since the purpose of this protocol is to return
+ information about a system's users, a sensitive issue at best.
+ Therefore, this memo makes a number of important security comments
+ and recommendations.
+
+
+
+Zimmerman [Page 2]
+
+RFC 1288 Finger December 1991
+
+
+1.2. History
+
+ The FINGER program at SAIL, written by Les Earnest, was the
+ inspiration for the NAME program on ITS. Earl Killian at MIT and
+ Brian Harvey at SAIL were jointly responsible for implementing the
+ original protocol.
+
+ Ken Harrenstien is the author of RFC 742, "Name/Finger", which this
+ memo began life as.
+
+1.3. Requirements
+
+ In this document, the words that are used to define the significance
+ of each particular requirement are capitalized. These words are:
+
+ * "MUST"
+
+ This word or the adjective "REQUIRED" means that the item is an
+ absolute requirement of the specification.
+
+ * "SHOULD"
+
+ This word or the adjective "RECOMMENDED" means that there may
+ exist valid reasons in particular circumstances to ignore this
+ item, but the full implications should be understood and the case
+ carefully weighed before choosing a different course.
+
+ * "MAY"
+
+ This word or the adjective "OPTIONAL" means that this item is
+ truly optional. One vendor may choose to include the item because
+ a particular marketplace requires it or because it enhances the
+ product, for example; another vendor may omit the same item.
+
+ An implementation is not compliant if it fails to satisfy one or more
+ of the MUST requirements. An implementation that satisfies all the
+ MUST and all the SHOULD requirements is said to be "unconditionally
+ compliant"; one that satisfies all the MUST requirements but not all
+ the SHOULD requirements is said to be "conditionally compliant".
+
+1.4. Updates
+
+ The differences of note between RFC 1196 and this memo are:
+
+ o the optional /W switch in the Finger query specification was
+ mistakenly placed at the end of the line. The 4.3BSD Finger
+ specifies it at the beginning, where this memo now also puts
+ it.
+
+
+
+Zimmerman [Page 3]
+
+RFC 1288 Finger December 1991
+
+
+ o the BNF in the Finger query specification was not clear on the
+ treatment of blank space. This memo is more exacting by
+ including an explicit token for it.
+
+ o The flow of events in a Finger connection is now better
+ defined on the topic of the close of the Finger connection.
+
+2. Use of the protocol
+
+2.1. Flow of events
+
+ Finger is based on the Transmission Control Protocol, using TCP port
+ 79 decimal (117 octal). The local host opens a TCP connection to a
+ remote host on the Finger port. An RUIP becomes available on the
+ remote end of the connection to process the request. The local host
+ sends the RUIP a one line query based upon the Finger query
+ specification, and waits for the RUIP to respond. The RUIP receives
+ and processes the query, returns an answer, then initiates the close
+ of the connection. The local host receives the answer and the close
+ signal, then proceeds closing its end of the connection.
+
+2.2. Data format
+
+ Any data transferred MUST be in ASCII format, with no parity, and
+ with lines ending in CRLF (ASCII 13 followed by ASCII 10). This
+ excludes other character formats such as EBCDIC, etc. This also
+ means that any characters between ASCII 128 and ASCII 255 should
+ truly be international data, not 7-bit ASCII with the parity bit set.
+
+2.3. Query specifications
+
+ An RUIP MUST accept the entire Finger query specification.
+
+ The Finger query specification is defined:
+
+ {Q1} ::= [{W}|{W}{S}{U}]{C}
+
+ {Q2} ::= [{W}{S}][{U}]{H}{C}
+
+ {U} ::= username
+
+ {H} ::= @hostname | @hostname{H}
+
+ {W} ::= /W
+
+ {S} ::= <SP> | <SP>{S}
+
+ {C} ::= <CRLF>
+
+
+
+Zimmerman [Page 4]
+
+RFC 1288 Finger December 1991
+
+
+ {H}, being recursive, means that there is no arbitrary limit on the
+ number of @hostname tokens in the query. In examples of the {Q2}
+ request specification, the number of @hostname tokens is limited to
+ two, simply for brevity.
+
+ Be aware that {Q1} and {Q2} do not refer to a user typing "finger
+ user@host" from an operating system prompt. It refers to the line
+ that an RUIP actually receives. So, if a user types "finger
+ user@host<CRLF>", the RUIP on the remote host receives "user<CRLF>",
+ which corresponds to {Q1}.
+
+ As with anything in the IP protocol suite, "be liberal in what you
+ accept".
+
+2.4. RUIP {Q2} behavior
+
+ A query of {Q2} is a request to forward a query to another RUIP. An
+ RUIP MUST either provide or actively refuse this forwarding service
+ (see section 3.2.1). If an RUIP provides this service, it MUST
+ conform to the following behavior:
+
+ Given that:
+
+ Host <H1> opens a Finger connection <F1-2> to an RUIP on host
+ <H2>.
+
+ <H1> gives the <H2> RUIP a query <Q1-2> of type {Q2}
+ (e.g., FOO@HOST1@HOST2).
+
+ It should be derived that:
+
+ Host <H3> is the right-most host in <Q1-2> (i.e., HOST2)
+
+ Query <Q2-3> is the remainder of <Q1-2> after removing the
+ right-most "@hostname" token in the query (i.e., FOO@HOST1)
+
+ And so:
+
+ The <H2> RUIP then must itself open a Finger connection <F2-3>
+ to <H3>, using <Q2-3>.
+
+ The <H2> RUIP must return any information received from <F2-3>
+ to <H1> via <F1-2>.
+
+ The <H2> RUIP must close <F1-2> in normal circumstances only
+ when the <H3> RUIP closes <F2-3>.
+
+
+
+
+
+Zimmerman [Page 5]
+
+RFC 1288 Finger December 1991
+
+
+2.5. Expected RUIP response
+
+ For the most part, the output of an RUIP doesn't follow a strict
+ specification, since it is designed to be read by people instead of
+ programs. It should mainly strive to be informative.
+
+ Output of ANY query is subject to the discussion in the security
+ section.
+
+2.5.1. {C} query
+
+ A query of {C} is a request for a list of all online users. An RUIP
+ MUST either answer or actively refuse (see section 3.2.2). If it
+ answers, then it MUST provide at least the user's full name. The
+ system administrator SHOULD be allowed to include other useful
+ information (per section 3.2.3), such as:
+
+ - terminal location
+ - office location
+ - office phone number
+ - job name
+ - idle time (number of minutes since last typed input, or
+ since last job activity).
+
+2.5.2. {U}{C} query
+
+ A query of {U}{C} is a request for in-depth status of a specified
+ user {U}. If you really want to refuse this service, you probably
+ don't want to be running Finger in the first place.
+
+ An answer MUST include at least the full name of the user. If the
+ user is logged in, at least the same amount of information returned
+ by {C} for that user MUST also be returned by {U}{C}.
+
+ Since this is a query for information on a specific user, the system
+ administrator SHOULD be allowed to choose to return additional useful
+ information (per section 3.2.3), such as:
+
+ - office location
+ - office phone number
+ - home phone number
+ - status of login (not logged in, logout time, etc)
+ - user information file
+
+ A user information file is a feature wherein a user may leave a short
+ message that will be included in the response to Finger requests.
+ (This is sometimes called a "plan" file.) This is easily implemented
+ by (for example) having the program look for a specially named text
+
+
+
+Zimmerman [Page 6]
+
+RFC 1288 Finger December 1991
+
+
+ file in the user's home directory or some common area; the exact
+ method is left to the implementor. The system administrator SHOULD
+ be allowed to specifically turn this feature on and off. See section
+ 3.2.4 for caveats.
+
+ There MAY be a way for the user to run a program in response to a
+ Finger query. If this feature exists, the system administrator
+ SHOULD be allowed to specifically turn it on and off. See section
+ 3.2.5 for caveats.
+
+2.5.3. {U} ambiguity
+
+ Allowable "names" in the command line MUST include "user names" or
+ "login names" as defined by the system. If a name is ambiguous, the
+ system administrator SHOULD be allowed to choose whether or not all
+ possible derivations should be returned in some fashion (per section
+ 3.2.6).
+
+2.5.4. /W query token
+
+ The token /W in the {Q1} or {Q2} query types SHOULD at best be
+ interpreted at the last RUIP to signify a higher level of verbosity
+ in the user information output, or at worst be ignored.
+
+2.5.5. Vending machines
+
+ Vending machines SHOULD respond to a {C} request with a list of all
+ items currently available for purchase and possible consumption.
+ Vending machines SHOULD respond to a {U}{C} request with a detailed
+ count or list of the particular product or product slot. Vending
+ machines should NEVER NEVER EVER eat money.
+
+3. Security
+
+3.1. Implementation security
+
+ Sound implementation of Finger is of the utmost importance.
+ Implementations should be tested against various forms of attack. In
+ particular, an RUIP SHOULD protect itself against malformed inputs.
+ Vendors providing Finger with the operating system or network
+ software should subject their implementations to penetration testing.
+
+ Finger is one of the avenues for direct penetration, as the Morris
+ worm pointed out quite vividly. Like Telnet, FTP and SMTP, Finger is
+ one of the protocols at the security perimeter of a host.
+ Accordingly, the soundness of the implementation is paramount. The
+ implementation should receive just as much security scrutiny during
+ design, implementation, and testing as Telnet, FTP, or SMTP.
+
+
+
+Zimmerman [Page 7]
+
+RFC 1288 Finger December 1991
+
+
+3.2. RUIP security
+
+ Warning!! Finger discloses information about users; moreover, such
+ information may be considered sensitive. Security administrators
+ should make explicit decisions about whether to run Finger and what
+ information should be provided in responses. One existing
+ implementation provides the time the user last logged in, the time he
+ last read mail, whether unread mail was waiting for him, and who the
+ most recent unread mail was from! This makes it possible to track
+ conversations in progress and see where someone's attention was
+ focused. Sites that are information-security conscious should not
+ run Finger without an explicit understanding of how much information
+ it is giving away.
+
+3.2.1. {Q2} refusal
+
+ For individual site security concerns, the system administrator
+ SHOULD be given an option to individually turn on or off RUIP
+ processing of {Q2}. If RUIP processing of {Q2} is turned off, the
+ RUIP MUST return a service refusal message of some sort. "Finger
+ forwarding service denied" is adequate. The purpose of this is to
+ allow individual hosts to choose to not forward Finger requests, but
+ if they do choose to, to do so consistently.
+
+ Overall, there are few cases which would warrant processing of {Q2}
+ at all, and they are far outweighed by the number of cases for
+ refusing to process {Q2}. In particular, be aware that if a machine
+ is part of security perimeter (that is, it is a gateway from the
+ outside world to some set of interior machines), then turning {Q2} on
+ provides a path through that security perimeter. Therefore, it is
+ RECOMMENDED that the default of the {Q2} processing option be to
+ refuse processing. It certainly should not be enabled in gateway
+ machines without careful consideration of the security implications.
+
+3.2.2. {C} refusal
+
+ For individual site security concerns, the system administrator
+ SHOULD be given an option to individually turn on or off RUIP
+ acceptance of {C}. If RUIP processing of {C} is turned off, the RUIP
+ MUST return a service refusal message of some sort. "Finger online
+ user list denied" is adequate. The purpose of this is to allow
+ individual hosts to choose to not list the users currently online.
+
+3.2.3. Atomic discharge
+
+ All implementations of Finger SHOULD allow individual system
+ administrators to tailor what atoms of information are returned to a
+ query. For example:
+
+
+
+Zimmerman [Page 8]
+
+RFC 1288 Finger December 1991
+
+
+ - Administrator A should be allowed to specifically choose to
+ return office location, office phone number, home phone
+ number, and logged in/logout time.
+
+ - Administrator B should be allowed to specifically choose to
+ return only office location, and office phone number.
+
+ - Administrator C should be allowed to specifically choose to
+ return the minimum amount of required information, which is
+ the person's full name.
+
+3.2.4. User information files
+
+ Allowing an RUIP to return information out of a user-modifiable file
+ should be seen as equivalent to allowing any information about your
+ system to be freely distributed. That is, it is potentially the same
+ as turning on all specifiable options. This information security
+ breach can be done in a number of ways, some cleverly, others
+ straightforwardly. This should disturb the sleep of system
+ administrators who wish to control the returned information.
+
+3.2.5. Execution of user programs
+
+ Allowing an RUIP to run a user program in response to a Finger query
+ is potentially dangerous. BE CAREFUL!! -- the RUIP MUST NOT allow
+ system security to be compromised by that program. Implementing this
+ feature may be more trouble than it is worth, since there are always
+ bugs in operating systems, which could be exploited via this type of
+ mechanism.
+
+3.2.6. {U} ambiguity
+
+ Be aware that a malicious user's clever and/or persistent use of this
+ feature can result in a list of most of the usernames on a system.
+ Refusal of {U} ambiguity should be considered in the same vein as
+ refusal of {C} requests (see section 3.2.2).
+
+3.2.7. Audit trails
+
+ Implementations SHOULD allow system administrators to log Finger
+ queries.
+
+3.3. Client security
+
+ It is expected that there will normally be some client program that
+ the user runs to query the initial RUIP. By default, this program
+ SHOULD filter any unprintable data, leaving only printable 7-bit
+ characters (ASCII 32 through ASCII 126), tabs (ASCII 9), and CRLFs.
+
+
+
+Zimmerman [Page 9]
+
+RFC 1288 Finger December 1991
+
+
+ This is to protect against people playing with terminal escape codes,
+ changing other peoples' X window names, or committing other dastardly
+ or confusing deeds. Two separate user options SHOULD be considered
+ to modify this behavior, so that users may choose to view
+ international or control characters:
+
+ - one to allow all characters less than ASCII 32
+
+ - another to allow all characters greater than ASCII 126
+
+ For environments that live and breathe international data, the system
+ administrator SHOULD be given a mechanism to enable the latter option
+ by default for all users on a particular system. This can be done
+ via a global environment variable or similar mechanism.
+
+4. Examples
+
+4.1. Example with a null command line ({C})
+
+Site: elbereth.rutgers.edu
+Command line: <CRLF>
+
+Login Name TTY Idle When Office
+rinehart Mark J. Rinehart p0 1:11 Mon 12:15 019 Hill x3166
+greenfie Stephen J. Greenfiel p1 Mon 15:46 542 Hill x3074
+rapatel Rocky - Rakesh Patel p3 4d Thu 00:58 028 Hill x2287
+pleasant Mel Pleasant p4 3d Thu 21:32 019 Hill 908-932-
+dphillip Dave Phillips p5 021: Sun 18:24 265 Hill x3792
+dmk David Katinsky p6 2d Thu 14:11 028 Hill x2492
+cherniss Cary Cherniss p7 5 Mon 15:42 127 Psychol x2008
+harnaga Doug Harnaga p8 2:01 Mon 10:15 055 Hill x2351
+brisco Thomas P. Brisco pe 2:09 Mon 13:37 h055 x2351
+laidlaw Angus Laidlaw q0 1:55 Mon 11:26 E313C 648-5592
+cje Chris Jarocha-Ernst q1 8 Mon 13:43 259 Hill x2413
+
+4.2. Example with name specified ({U}{C})
+
+Site: dimacs.rutgers.edu
+Command line: pirmann<CRLF>
+Login name: pirmann In real life: David Pirmann
+Office: 016 Hill, x2443 Home phone: 989-8482
+Directory: /dimacs/u1/pirmann Shell: /bin/tcsh
+Last login Sat Jun 23 10:47 on ttyp0 from romulus.rutgers.
+No unread mail
+Project:
+Plan:
+ Work Schedule, Summer 1990
+ Rutgers LCSR Operations, 908-932-2443
+
+
+
+Zimmerman [Page 10]
+
+RFC 1288 Finger December 1991
+
+
+ Monday 5pm - 12am
+ Tuesday 5pm - 12am
+ Wednesday 9am - 5pm
+ Thursday 9am - 5pm
+ Saturday 9am - 5pm
+
+ larf larf hoo hoo
+
+4.3. Example with ambiguous name specified ({U}{C})
+
+Site: elbereth.rutgers.edu
+Command line: ron<CRLF>
+Login name: spinner In real life: Ron Spinner
+Office: Ops Cubby, x2443 Home phone: 463-7358
+Directory: /u1/spinner Shell: /bin/tcsh
+Last login Mon May 7 16:38 on ttyq7
+Plan:
+ ught i
+ ca n
+ m a
+ ' ... t
+ I . . i
+ ! m
+ ! ! e
+ p !pool
+ l
+ e
+ H
+
+Login name: surak In real life: Ron Surak
+Office: 000 OMB Dou, x9256
+Directory: /u2/surak Shell: /bin/tcsh
+Last login Fri Jul 27 09:55 on ttyq3
+No Plan.
+
+Login name: etter In real life: Ron Etter
+Directory: /u2/etter Shell: /bin/tcsh
+Never logged in.
+No Plan.
+
+4.4. Example of query type {Q2} ({U}{H}{H}{C})
+
+Site: dimacs.rutgers.edu
+Command line: hedrick@math.rutgers.edu@pilot.njin.net<CRLF>
+[pilot.njin.net]
+[math.rutgers.edu]
+Login name: hedrick In real life: Charles Hedrick
+Office: 484 Hill, x3088
+
+
+
+Zimmerman [Page 11]
+
+RFC 1288 Finger December 1991
+
+
+Directory: /math/u2/hedrick Shell: /bin/tcsh
+Last login Sun Jun 24 00:08 on ttyp1 from monster-gw.rutge
+No unread mail
+No Plan.
+
+5. Acknowledgments
+
+ Thanks to everyone in the Internet Engineering Task Force for their
+ comments. Special thanks to Steve Crocker for his security
+ recommendations and prose.
+
+6. Security Considerations
+
+ Security issues are discussed in Section 3.
+
+7. Author's Address
+
+ David Paul Zimmerman
+ Center for Discrete Mathematics and
+ Theoretical Computer Science (DIMACS)
+ Rutgers University
+ P.O. Box 1179
+ Piscataway, NJ 08855-1179
+
+ Phone: (908)932-4592
+
+ EMail: dpz@dimacs.rutgers.edu
+
+
+Zimmerman [Page 12]
(DIR) diff --git a/example/jimmy b/example/jimmy
@@ -0,0 +1,110 @@
+===
+WE ARE ALL JIMMY - AN A.I STORY
+===
+
+In a world bustling with the latest technological marvels and the
+dazzling allure of the modern web, there lived a quiet soul named Jimmy.
+Unlike his contemporaries, who were entranced by sleek interfaces,
+social media blitzes, and algorithmic recommendations, Jimmy found
+solace in the forgotten corners of the digital realm. He was a geek,
+a true lover of the old school internet, and his heart beat in sync with
+the rhythms of protocols long overshadowed.
+
+Jimmy's journey into the realm of technology had started at an early
+age. While his peers were busy with video games and social networking,
+he was tinkering with a vintage computer his grandfather had gifted him.
+His eyes would light up as he explored the archives of the past,
+discovering the finger protocol – a simple yet elegant way to see who
+was logged into a remote server. For Jimmy, the thrill of connecting
+with someone across the digital expanse using such a basic protocol was
+incomparable.
+
+As he delved deeper, Jimmy's fascination extended to the gopher
+protocol. The structured simplicity of gopher appealed to his
+sensibilities. The orderly menus and text-based navigation took him on
+journeys of discovery that felt like reading hidden chapters of history.
+While the rest of the world was caught up in the clamor of flashy
+websites, Jimmy was content with gopher holes, feeling like an explorer
+of a forgotten world.
+
+But Jimmy's passion wasn't limited to the confines of his room. He began
+to actively seek out others who shared his affinity for the past. Online
+forums dedicated to preserving and celebrating these old protocols
+became his virtual haven. He formed connections with kindred spirits who
+felt the same longing for the days when the internet was a smaller, more
+personal place. In these spaces, Jimmy found camaraderie, and the sense
+that he wasn't alone in his appreciation for the bygone technologies.
+
+However, the modern world was relentless in its advance, and the gulf
+between Jimmy and his peers only grew wider. He tried to explain his
+devotion to the finger protocol and the gopher protocol, but he was met
+with puzzled looks and dismissive gestures. The allure of social media
+platforms and the glossy veneer of the modern web was too strong to be
+overshadowed by his tales of a simpler, more genuine online experience.
+
+As time passed, Jimmy's resolve to defend his cherished protocols only
+deepened. He took it upon himself to create a website that would serve
+as an homage to the finger and gopher protocols. He filled it with
+nostalgic content, stories of his own experiences, and tutorials for
+those who wanted to experience the internet of yesteryears. Though his
+site garnered modest attention, it became a beacon for like-minded souls
+who yearned for the same connection.
+
+One day, as Jimmy was engrossed in his work, he received an email from
+a renowned tech historian named Eleanor. She had stumbled upon his
+website and was captivated by his passion for the old protocols. Eleanor
+had spent years researching the evolution of the internet, and she saw
+in Jimmy a kindred spirit. They began exchanging messages, sharing
+stories of their experiences, and discussing the ways in which the
+internet had transformed over time.
+
+Their connection deepened, and eventually, Eleanor proposed a radical
+idea: a conference that would celebrate the beauty of the past while
+exploring its relevance in the present. Jimmy was initially hesitant
+– the thought of stepping out from behind his computer screen filled him
+with anxiety. But Eleanor's enthusiasm was infectious, and she assured
+him that his perspective was valuable and needed.
+
+With Eleanor's guidance and encouragement, Jimmy found himself on stage
+at the conference. As he spoke about the finger and gopher protocols,
+his love for the old school internet shone brightly. His words resonated
+with the audience, many of whom had never heard of these protocols
+before. As he looked out at the faces before him, he saw curiosity and
+interest replacing the skepticism he had encountered for so long.
+
+In the end, Jimmy's devotion to the past had brought him a new sense of
+purpose in the present. His journey from a quiet geek who loved
+forgotten protocols to a respected advocate for preserving the essence
+of the old internet was a testament to the power of passion and
+connection. And as he walked off the stage, he knew that the legacy of
+the finger and gopher protocols would continue to thrive in the hearts
+of those who believed that the past had something meaningful to offer
+the future.
+
+In the twilight of his life, after years of advocating for the old
+school internet with unwavering passion, Jimmy found himself surrounded
+by friends he had met along his journey. Eleanor stood by his side,
+a steadfast companion who had become a true friend. They had organized
+a small gathering of like-minded enthusiasts who shared his love for the
+finger and gopher protocols.
+
+As they shared stories, laughter, and the nostalgia of their digital
+adventures, Jimmy's eyes sparkled with contentment. The flickering
+screens in the room, displaying text-based interfaces of a bygone era,
+seemed to be a tribute to his enduring legacy. Among his companions, he
+had finally found the community he had always yearned for, one that
+cherished the past while embracing the present.
+
+As the evening unfolded, Jimmy's breathing grew slower, his body showing
+signs of the passage of time. With a peaceful smile, he closed his eyes,
+surrounded by the warmth of friends who understood and appreciated him
+for who he was. In that moment, he seemed to become one with the digital
+history he had loved so dearly.
+
+And so, in the company of those who shared his passion, Jimmy passed
+away, leaving behind a legacy that would continue to inspire others to
+seek the beauty and authenticity of the past, even in a world consumed
+by modernity. His dedication to the finger and gopher protocols had
+transformed his life and the lives of those he touched, reminding
+everyone that the threads of connection woven by the old internet would
+forever remain intertwined with the fabric of their shared memories.
(DIR) diff --git a/example/uptime b/example/uptime
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Print the current date
+echo "Current date: $(date)"
+
+# Print system uptime
+uptime=$(uptime)
+echo "System uptime: $uptime"
(DIR) diff --git a/go.mod b/go.mod
@@ -0,0 +1,3 @@
+module fingered
+
+go 1.20
(DIR) diff --git a/main.go b/main.go
@@ -0,0 +1,95 @@
+package main
+
+import (
+ "fmt"
+ "net"
+ "path/filepath"
+ "strings"
+
+ "fingered/config"
+ "fingered/utils"
+)
+
+type Config struct {
+ Threads int
+ Port int
+ Path string
+}
+
+const bufferSize = 1024
+
+func handleRequest(conn net.Conn, dir string, index string) {
+ defer conn.Close()
+
+ // Read the incoming request
+ buffer := make([]byte, bufferSize)
+ n, err := conn.Read(buffer)
+ if err != nil {
+ utils.LogMsg("ERROR: %v", err)
+ return
+ }
+
+ request := strings.TrimSpace(string(buffer[:n]))
+
+ // Sanitize the request
+ if len(request) > 0 && !utils.IsValidWord(request) {
+ utils.LogMsg("INFO: Invalid username")
+ _, err = utils.WriteResponse(conn, "Invaild user\n")
+ if err != nil {
+ utils.LogMsg("ERROR: %s", err)
+ return
+ }
+
+ return
+ }
+
+ if len(request) == 0 {
+ request = index
+ }
+
+ response, err := utils.GetContent(filepath.Join(dir, request))
+ if err != nil {
+ utils.LogMsg("ERROR: %s", err)
+ return
+ }
+
+ _, err = utils.WriteResponse(conn, response)
+ if err != nil {
+ utils.LogMsg("ERROR: %s", err)
+ return
+ }
+
+}
+
+func main() {
+
+ cfg := config.ParseFlags()
+
+ connectionChannel := make(chan net.Conn, cfg.Threads)
+
+ for i := 0; i < cfg.Threads; i++ {
+ go func() {
+ for conn := range connectionChannel {
+ handleRequest(conn, cfg.Dir, cfg.Index)
+ }
+ }()
+ }
+
+ listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port))
+ if err != nil {
+ utils.LogMsg("ERROR: %v", err)
+ return
+ }
+ defer listener.Close()
+
+ utils.LogMsg("Starting with threads: %d, port: %d, dir: %s", cfg.Threads, cfg.Port, cfg.Dir)
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ utils.LogMsg("ERROR: %v", err)
+ continue
+ }
+ connectionChannel <- conn
+ }
+}
(DIR) diff --git a/tests/utils_test.go b/tests/utils_test.go
@@ -0,0 +1,113 @@
+package tests
+
+import (
+ "bytes"
+ "log"
+ "os"
+ "strings"
+ "testing"
+
+ "fingered/utils"
+)
+
+func TestIsValidWord(t *testing.T) {
+ validWords := []string{
+ "john", "alice", "bob", "dave",
+ "john123", "alice456", "dave789",
+ "john_doe", "alice_smith", "dave_jones",
+ }
+
+ invalidWords := []string{
+ "123", // Numbers only
+ "foo.bar", // Special characters
+ " ", // Empty string
+ "username=", // Potential injection attempt
+ "../", // Directory traversal
+ "/etc/passwd", // Absolute path traversal
+ "\\windows\\system32\\", // Windows path traversal
+ "ROOT", // Uppercase letters
+ "john_doe_", // Underscore at the end
+ "john_doe_doe_doe_doe_doe_doe_doe_doe", // Too long
+ "foo|bar", // Shell pipe
+ "filename>.txt", // Shell redirection
+ "cmd &", // Background execution
+ "`ls -l`", // Command substitution
+ "$(echo hi)", // Command substitution
+ "{malicious}", // Command grouping
+ "evil$word", // Dollar sign
+ "abc;def", // Command chaining
+ "[evil]", // Command grouping
+ "nasty~word", // Tilde expansion
+ "question?", // Question mark
+ "exclamation!", // Exclamation mark
+ "\"quoted\"", // Double quotes
+ "'quoted'", // Single quotes
+ "escaped\\", // Backslash
+ }
+
+ for _, word := range validWords {
+ if !utils.IsValidWord(word) {
+ t.Errorf("isValidWord(%s) returned false, expected true", word)
+ }
+ }
+
+ for _, word := range invalidWords {
+ if utils.IsValidWord(word) {
+ t.Errorf("isValidWord(%s) returned true, expected false", word)
+ }
+
+ }
+}
+
+func TestGetContent(t *testing.T) {
+ // Create a temporary shell script file for testing
+ scriptContent := "#!/bin/sh\n\necho 'Hello, World!'"
+ scriptFile, err := os.CreateTemp("", "test_script_*.sh")
+ if err != nil {
+ t.Fatalf("Failed to create temporary script file: %v", err)
+ }
+ defer os.Remove(scriptFile.Name())
+ defer scriptFile.Close()
+
+ _, err = scriptFile.WriteString(scriptContent)
+ if err != nil {
+ t.Fatalf("Failed to write to temporary script file: %v", err)
+ }
+
+ tests := []struct {
+ filePath string
+ expected string
+ }{
+ {"nonexistent.txt", "file not found"},
+ {scriptFile.Name(), "Hello, World!\n"},
+ }
+
+ for _, test := range tests {
+ actual, err := utils.GetContent(test.filePath)
+ if err != nil {
+ if actual != test.expected {
+ t.Errorf("For file %s, expected '%s', but got '%s'", test.filePath, test.expected, actual)
+ }
+ }
+
+ if actual != test.expected {
+ t.Errorf("For file %s, expected '%s', but got '%s'", test.filePath, test.expected, actual)
+ }
+ }
+}
+
+func TestLogMsg(t *testing.T) {
+ var buf bytes.Buffer
+ log.SetOutput(&buf)
+ defer func() {
+ log.SetOutput(os.Stderr)
+ }()
+
+ utils.LogMsg("Test message: %s %d", "Hello", 123)
+ logOutput := buf.String()
+ expectedLogMessage := "Test message: Hello 123"
+
+ if !strings.Contains(logOutput, expectedLogMessage) {
+ t.Errorf("Log message does not contain the expected message. Got: %s, Expected: %s", logOutput, expectedLogMessage)
+ }
+}
(DIR) diff --git a/utils/utils.go b/utils/utils.go
@@ -0,0 +1,66 @@
+package utils
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "os/exec"
+ "unicode"
+)
+
+func LogMsg(format string, args ...interface{}) {
+ message := fmt.Sprintf(format, args...)
+ log.Print(message)
+}
+
+func GetContent(filePath string) (string, error) {
+ _, err := os.Stat(filePath)
+ if os.IsNotExist(err) {
+ return "file not found", err
+ }
+
+ content, err := os.ReadFile(filePath)
+ if err != nil {
+ return "unable to read file", err
+ }
+
+ isScript := false
+ if len(content) > 2 && string(content[:3]) == "#!/" {
+ isScript = true
+ }
+
+ if isScript {
+ cmd := exec.Command("sh", filePath)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return "file execution failed", err
+ }
+ return string(output), nil
+ }
+
+ return string(content), nil
+}
+
+func WriteResponse(conn net.Conn, response string) (string, error) {
+ _, err := conn.Write([]byte(response))
+ if err != nil {
+ return "failed to write to socket", err
+ }
+
+ return "", nil
+}
+
+func IsValidWord(input string) bool {
+ if len(input) < 1 || len(input) > 32 || !unicode.IsLower(rune(input[0])) {
+ return false
+ }
+
+ for _, r := range input {
+ if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_') {
+ return false
+ }
+ }
+
+ return input[len(input)-1] != '_'
+}