Add project files
This commit is contained in:
parent
d93b571440
commit
2dd8f72714
9 changed files with 260 additions and 1 deletions
119
ssh.go
Normal file
119
ssh.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package main
|
||||
|
||||
// Copyright (c) 2024 Julian Müller (ChaoticByte)
|
||||
// License: MIT
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
)
|
||||
|
||||
var sessionListMutex = &sync.Mutex{}
|
||||
var sessionList = []*ssh.Session{}
|
||||
|
||||
func PubkeyAuth(ctx ssh.Context, key ssh.PublicKey) bool {
|
||||
// verify public key for auth
|
||||
u := ctx.User()
|
||||
pubkey := config.Clients[u]
|
||||
if pubkey == "" { // username not in config.Clients
|
||||
log.Printf("Authentication failure: Unknown user %s", u)
|
||||
return false
|
||||
}
|
||||
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
|
||||
if err != nil {
|
||||
log.Printf("Authentication failure: Could not parse public key for %s", u)
|
||||
}
|
||||
if ssh.KeysEqual(k, key) {
|
||||
return true // bassd
|
||||
} else {
|
||||
log.Printf("Authentication failure: Invalid public key for user %s", u)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func AddToSessionList(s *ssh.Session) int {
|
||||
// add new session to list
|
||||
var idx int = -1
|
||||
sessionListMutex.Lock()
|
||||
defer sessionListMutex.Unlock()
|
||||
lenClientList := len(sessionList)
|
||||
// try to reuse an existing nil position in the slice
|
||||
for i := range(lenClientList) {
|
||||
if sessionList[i] == nil {
|
||||
// we found one!
|
||||
sessionList[i] = s
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx == -1 {
|
||||
// we have to append
|
||||
sessionList = append(sessionList, s)
|
||||
idx = lenClientList
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
func RemoveFromSessionList(idx int) {
|
||||
sessionListMutex.Lock()
|
||||
defer sessionListMutex.Unlock()
|
||||
// we have to set it to nil instead of constructing a new
|
||||
// slice, so that the other session handlers have still
|
||||
// the correct index for their own removal
|
||||
sessionList[idx] = nil // session associated with the pointer reference should be freed automatically
|
||||
}
|
||||
|
||||
func BroadcastLine(line []byte) {
|
||||
if logFlag { log.Printf("%s", line) } // output message
|
||||
sessions := sessionList // create snapshot of client list
|
||||
for i := range(len(sessions)) {
|
||||
session := sessions[i]
|
||||
if session != nil {
|
||||
(*session).Write(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSession(session ssh.Session) {
|
||||
// get username
|
||||
user := session.Context().User()
|
||||
msgPrefix := []byte(fmt.Sprintf("%s: ", user))
|
||||
// Add client to list
|
||||
sessionListIdx := AddToSessionList(&session)
|
||||
defer func () { // cleanup tasks
|
||||
RemoveFromSessionList(sessionListIdx)
|
||||
log.Printf("User %s disconnected.\n", user)
|
||||
BroadcastLine([]byte(fmt.Sprintf("[disconnected] %s\n", user)))
|
||||
}()
|
||||
// helo
|
||||
log.Printf("User %s connected.\n", user)
|
||||
BroadcastLine([]byte(fmt.Sprintf("[connected] %s\n", user)))
|
||||
// IO
|
||||
linebuf := bufio.NewReader(session)
|
||||
for {
|
||||
line, err := linebuf.ReadBytes('\n')
|
||||
if err != nil {
|
||||
if err.Error() != "EOF" {
|
||||
log.Println("An error occurred! ", user, " ", err)
|
||||
}
|
||||
break // disconnect
|
||||
} else {
|
||||
BroadcastLine(slices.Concat(msgPrefix, line))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RunServer() {
|
||||
log.Printf("Starting SSH server on %s:%v", config.Host, config.Port)
|
||||
log.Fatal(ssh.ListenAndServe(
|
||||
fmt.Sprintf("%s:%v", config.Host, config.Port),
|
||||
HandleSession,
|
||||
ssh.HostKeyFile(privateKeyFilepath),
|
||||
ssh.PublicKeyAuth(PubkeyAuth),
|
||||
ssh.NoPty()))
|
||||
}
|
Reference in a new issue