This repository has been archived by the owner on Jul 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssh.go
120 lines (110 loc) · 2.95 KB
/
ssh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
sessionListMutex.Lock()
defer sessionListMutex.Unlock()
for i := range(len(sessionList)) {
session := sessionList[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()))
}