about summary refs log tree commit diff
diff options
context:
space:
mode:
authormaride <maride@darknebu.la>2018-08-14 13:57:13 +0200
committermaride <maride@darknebu.la>2018-08-14 13:57:13 +0200
commitcfb006c2be8717ebd10d39b9dd64211ff7ce1eb0 (patch)
tree3fe376f90c6caf932a7cc32cb3f9b60c15385f69
parent61c8c9d29824a7ec1ebfb96413ce1713260dfd57 (diff)
Add Login/Logout/Sessions using given access codes
-rw-r--r--README.md11
-rw-r--r--src/credentials.go18
-rw-r--r--src/http.go143
-rw-r--r--src/main.go17
-rw-r--r--src/session.go60
5 files changed, 249 insertions, 0 deletions
diff --git a/README.md b/README.md
index 9e218b7..433f658 100644
--- a/README.md
+++ b/README.md
@@ -7,3 +7,14 @@ The companion ~cube~ container spawned specifically for one user.
 - Validator for flags
 - Temporary secret vault for the user
 - control endpoint of challenge containers for the user
+
+## Usage
+
+This executable needs some parameters to work properly:
+
+| Key | Required? | Description |
+|---|---|---|
+| `-port` | No | The port for HTTP. *Default: 8080* |
+| `-username` | No | Name of our user, as used e.g. in salutation. No length or charset limitation. *Default: Player 1* |
+| `-accessCode` | Yes | Access code for the user. *Default: AllYourCodesAreBelongToUs* |
+| `-sessionSalt` | Yes | Variable to salt the session token generator with. |
diff --git a/src/credentials.go b/src/credentials.go
new file mode 100644
index 0000000..7bbcf19
--- /dev/null
+++ b/src/credentials.go
@@ -0,0 +1,18 @@
+package main
+
+import "flag"
+
+var (
+	username* string
+	accessCode* string
+)
+
+func registerCredentialsFlags() {
+	username = flag.String("username", "Player 1", "Username")
+	accessCode = flag.String("accessCode", "AllYourCodesAreBelongToUs", "Access code for the user")
+}
+
+
+func verifyCredentials(un string, ac string) (bool) {
+	return *username == un && *accessCode == ac
+}
\ No newline at end of file
diff --git a/src/http.go b/src/http.go
new file mode 100644
index 0000000..9f97352
--- /dev/null
+++ b/src/http.go
@@ -0,0 +1,143 @@
+package main
+
+import (
+	"flag"
+	"github.com/gorilla/mux"
+	"net/http"
+	"fmt"
+	"strings"
+	"io/ioutil"
+	"time"
+)
+
+var (
+	port *int
+)
+
+func registerHTTPFlags() {
+	port = flag.Int("port", 8080, "The port for HTTP")
+}
+
+func runHTTPServer() (error) {
+	r := mux.NewRouter()
+
+	r.HandleFunc("/", indexHandler)
+	r.HandleFunc("/files/{file}", fileHandler)
+	r.HandleFunc("/login", loginGetHandler).Methods("GET")
+	r.HandleFunc("/login", loginPostHandler).Methods("POST")
+	r.HandleFunc("/logout", logoutHandler).Methods("GET")
+
+	address := fmt.Sprintf(":%d", *port)
+	return http.ListenAndServe(address, r)
+}
+
+// Host the index file
+func indexHandler(w http.ResponseWriter, r *http.Request) {
+	session, cookieNotFoundError := r.Cookie("session")
+
+	if cookieNotFoundError != nil || !isValidSession(session.Value) {
+		// either no session cookie found, or it contains an invalid session token. Redirect.
+		http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
+	} else {
+		// valid session token found, redirect to frontpage
+		readFileToResponse(w, "/index.html")
+	}
+}
+
+// Static host files
+func fileHandler(w http.ResponseWriter, r *http.Request) {
+	readFileToResponse(w, mux.Vars(r)["file"])
+}
+
+// Helper function to host files off of "hosted/" directory
+func readFileToResponse(w http.ResponseWriter, path string) {
+	requestedFile := strings.Replace(path, "..", "", -1)
+
+	contents, readError := ioutil.ReadFile(fmt.Sprintf("hosted/%s", requestedFile))
+
+	if readError != nil {
+		w.Write([]byte(fmt.Sprintf("unable to read %s", requestedFile)))
+	} else {
+		w.Write([]byte(contents))
+	}
+}
+
+// Read login page
+func loginGetHandler(w http.ResponseWriter, r *http.Request) {
+	session, cookieNotFoundError := r.Cookie("session")
+
+	// we need to verify that the client doesn't already have a valid session
+	if cookieNotFoundError == nil && isValidSession(session.Value) {
+		// session already valid, redirect the user
+		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+	} else {
+		readFileToResponse(w, "/login.html")
+	}
+}
+
+// Process login data
+func loginPostHandler(w http.ResponseWriter, r *http.Request) {
+	r.ParseForm()
+	username := r.Form.Get("username")
+	accessCode := r.Form.Get("accesscode")
+	session, cookieNotFoundError := r.Cookie("session")
+
+	validRedirect := false
+
+	// we need to verify that the client doesn't already have a valid session
+	if cookieNotFoundError == nil && isValidSession(session.Value) {
+		// session already valid, redirect the user
+		validRedirect = true
+	} else {
+		// no valid session present, check the given credentials
+
+		if verifyCredentials(username, accessCode) {
+			// credentials valid, create session
+			newSessionToken := createSession()
+			http.SetCookie(w, &http.Cookie{
+				Name: "session",
+				Value: newSessionToken,
+				Path: "/",
+				Expires: time.Now().Add(time.Hour * 24),
+			})
+			validRedirect = true
+		}
+	}
+
+	if validRedirect {
+		// Redirect to frontpage
+		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+		return
+	} else {
+		// credentials invalid or database error occured
+		w.Write([]byte("Wrong credentials"))
+		return
+	}
+
+	// Something else weird happened.
+	w.WriteHeader(500)
+	w.Write([]byte("Server Error"))
+}
+
+// Process logout and deletion of cookie
+func logoutHandler(w http.ResponseWriter, r *http.Request) {
+	session, cookieNotFoundError := r.Cookie("session")
+
+	if cookieNotFoundError == nil {
+		// cookie was found
+		if isValidSession(session.Value) {
+			// and it contains a valid session. Delete the session...
+			destroySession(session.Value)
+		}
+
+		// and delete the cookie
+		http.SetCookie(w, &http.Cookie{
+			Name: "session",
+			Value: "",
+			Path: "/",
+			Expires: time.Unix(0, 0),
+		})
+	}
+
+	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+}
diff --git a/src/main.go b/src/main.go
new file mode 100644
index 0000000..519943a
--- /dev/null
+++ b/src/main.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+	"flag"
+	"log"
+)
+
+func main() {
+	// Set up flags
+	registerHTTPFlags()
+	registerSessionFlags()
+	registerCredentialsFlags()
+	flag.Parse()
+
+	// Run HTTP server
+	log.Fatalln(runHTTPServer())
+}
\ No newline at end of file
diff --git a/src/session.go b/src/session.go
new file mode 100644
index 0000000..bfaf685
--- /dev/null
+++ b/src/session.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+	"crypto/sha512"
+	"math/rand"
+	"encoding/base64"
+	"flag"
+)
+
+var (
+	tokenSalt *string
+	sessions = []string{}
+)
+
+func registerSessionFlags() {
+	tokenSalt = flag.String("sessionSalt", "", "Salt for the session token")
+}
+
+// Generates a random token
+func randToken() string {
+	// Generating the random part
+	randBuff := make([]byte, 128)
+	rand.Read(randBuff)
+
+	// Hashing the random part
+	hasher := sha512.New()
+	hasher.Write(randBuff)
+	hasher.Write([]byte(*tokenSalt))
+	hashBuff := hasher.Sum(nil)
+
+	// Returning that
+	return base64.URLEncoding.EncodeToString(hashBuff)
+}
+
+// Verifies a given session token against the internal session array
+func isValidSession(sessionToken string) (bool){
+	for _, token := range sessions {
+		if token == sessionToken {
+			return true
+		}
+	}
+	return false
+}
+
+// Generates a new session token and adds it to the internal session array
+func createSession() (string) {
+	newToken := randToken()
+	sessions = append(sessions, newToken)
+	return newToken
+}
+
+// Removes a session from the internal session array
+func destroySession(sessionToken string) {
+	for index, token := range sessions {
+		if token == sessionToken {
+			// delete session from slice
+			sessions = append(sessions[:index], sessions[:index + 1] ...)
+		}
+	}
+}