mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-26 05:49:25 +01:00
do bcrypt in the client main routine
This commit is contained in:
parent
2229645a39
commit
be089e7f5f
13
config.json
Normal file
13
config.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{ "name": "irc.example.com",
|
||||||
|
"motd": "motd.txt",
|
||||||
|
"listeners": [
|
||||||
|
{ "address": "localhost:7777" },
|
||||||
|
{ "address": "[::1]:7777" } ],
|
||||||
|
"operators": [
|
||||||
|
{ "name": "root",
|
||||||
|
"password": "JDJhJDEwJFRWWGUya2E3Unk5bnZlb2o3alJ0ZnVQQm9ZVW1HOE53L29nVHg5QWh5TnpaMmtOaEwya1Vl" } ],
|
||||||
|
"debug": {
|
||||||
|
"net": true,
|
||||||
|
"client": false,
|
||||||
|
"channel": false,
|
||||||
|
"server": false } }
|
@ -1,12 +1,35 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.google.com/p/go.crypto/bcrypt"
|
||||||
|
"encoding/base64"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
"github.com/jlatt/ergonomadic/irc"
|
"github.com/jlatt/ergonomadic/irc"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func genPasswd(passwd string) {
|
||||||
|
log.Printf("encoding password \"%s\"\n", passwd)
|
||||||
|
crypted, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
encoded := base64.StdEncoding.EncodeToString(crypted)
|
||||||
|
fmt.Println(encoded)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config, err := irc.LoadConfig()
|
conf := flag.String("conf", "ergonomadic.json", "ergonomadic config file")
|
||||||
|
passwd := flag.String("genpasswd", "", "bcrypt a password")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *passwd != "" {
|
||||||
|
genPasswd(*passwd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := irc.LoadConfig(*conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
return
|
return
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.google.com/p/go.crypto/bcrypt"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -15,6 +16,8 @@ type Client struct {
|
|||||||
atime time.Time
|
atime time.Time
|
||||||
awayMessage string
|
awayMessage string
|
||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
|
checkPass chan CheckCommand
|
||||||
|
commands chan editableCommand
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
flags map[UserMode]bool
|
flags map[UserMode]bool
|
||||||
hasQuit bool
|
hasQuit bool
|
||||||
@ -36,17 +39,77 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
atime: now,
|
atime: now,
|
||||||
channels: make(ChannelSet),
|
channels: make(ChannelSet),
|
||||||
|
checkPass: make(chan CheckCommand),
|
||||||
|
commands: make(chan editableCommand),
|
||||||
ctime: now,
|
ctime: now,
|
||||||
flags: make(map[UserMode]bool),
|
flags: make(map[UserMode]bool),
|
||||||
phase: server.InitPhase(),
|
phase: server.InitPhase(),
|
||||||
server: server,
|
server: server,
|
||||||
}
|
}
|
||||||
client.socket = NewSocket(conn, client, server.commands)
|
client.socket = NewSocket(conn, client.commands)
|
||||||
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
|
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
|
||||||
|
go client.run()
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// command goroutine
|
||||||
|
//
|
||||||
|
|
||||||
|
type CheckCommand interface {
|
||||||
|
Response() editableCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckedPassCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
isRight bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckPassCommand struct {
|
||||||
|
hash []byte
|
||||||
|
password []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *CheckPassCommand) CheckPassword() bool {
|
||||||
|
return bcrypt.CompareHashAndPassword(cmd.hash, cmd.password) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *CheckPassCommand) Response() editableCommand {
|
||||||
|
return &CheckedPassCommand{
|
||||||
|
isRight: cmd.CheckPassword(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckOperCommand struct {
|
||||||
|
CheckPassCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckedOperCommand struct {
|
||||||
|
CheckedPassCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *CheckOperCommand) Response() editableCommand {
|
||||||
|
response := &CheckedOperCommand{}
|
||||||
|
response.isRight = cmd.CheckPassword()
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) run() {
|
||||||
|
for command := range client.commands {
|
||||||
|
command.SetClient(client)
|
||||||
|
client.server.commands <- command
|
||||||
|
|
||||||
|
switch command.(type) {
|
||||||
|
case *PassCommand, *OperCommand:
|
||||||
|
cmd := <-client.checkPass
|
||||||
|
response := cmd.Response()
|
||||||
|
response.SetClient(client)
|
||||||
|
client.server.commands <- response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// idle timer goroutine
|
// idle timer goroutine
|
||||||
//
|
//
|
||||||
@ -106,7 +169,6 @@ func (client *Client) destroy() {
|
|||||||
for channel := range client.channels {
|
for channel := range client.channels {
|
||||||
channel.Quit(client)
|
channel.Quit(client)
|
||||||
}
|
}
|
||||||
client.channels = nil
|
|
||||||
|
|
||||||
// clean up server
|
// clean up server
|
||||||
|
|
||||||
@ -116,22 +178,16 @@ func (client *Client) destroy() {
|
|||||||
|
|
||||||
if client.loginTimer != nil {
|
if client.loginTimer != nil {
|
||||||
client.loginTimer.Stop()
|
client.loginTimer.Stop()
|
||||||
client.loginTimer = nil
|
|
||||||
}
|
}
|
||||||
if client.idleTimer != nil {
|
if client.idleTimer != nil {
|
||||||
client.idleTimer.Stop()
|
client.idleTimer.Stop()
|
||||||
client.idleTimer = nil
|
|
||||||
}
|
}
|
||||||
if client.quitTimer != nil {
|
if client.quitTimer != nil {
|
||||||
client.quitTimer.Stop()
|
client.quitTimer.Stop()
|
||||||
client.quitTimer = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.socket.Close()
|
client.socket.Close()
|
||||||
|
|
||||||
client.socket = nil
|
|
||||||
client.server = nil
|
|
||||||
|
|
||||||
if DEBUG_CLIENT {
|
if DEBUG_CLIENT {
|
||||||
log.Printf("%s: destroyed", client)
|
log.Printf("%s: destroyed", client)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,24 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func decodePassword(password string) []byte {
|
||||||
|
if password == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(password)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Debug map[string]bool
|
Debug map[string]bool
|
||||||
Listeners []ListenerConfig
|
Listeners []ListenerConfig
|
||||||
@ -14,11 +28,19 @@ type Config struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conf *Config) PasswordBytes() []byte {
|
||||||
|
return decodePassword(conf.Password)
|
||||||
|
}
|
||||||
|
|
||||||
type OperatorConfig struct {
|
type OperatorConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conf *OperatorConfig) PasswordBytes() []byte {
|
||||||
|
return decodePassword(conf.Password)
|
||||||
|
}
|
||||||
|
|
||||||
type ListenerConfig struct {
|
type ListenerConfig struct {
|
||||||
Net string
|
Net string
|
||||||
Address string
|
Address string
|
||||||
@ -30,10 +52,10 @@ func (config *ListenerConfig) IsTLS() bool {
|
|||||||
return (config.Key != "") && (config.Certificate != "")
|
return (config.Key != "") && (config.Certificate != "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (config *Config, err error) {
|
func LoadConfig(filename string) (config *Config, err error) {
|
||||||
config = &Config{}
|
config = &Config{}
|
||||||
|
|
||||||
file, err := os.Open("ergonomadic.json")
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -44,6 +66,10 @@ func LoadConfig() (config *Config, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir := filepath.Dir(filename)
|
||||||
|
config.MOTD = filepath.Join(dir, config.MOTD)
|
||||||
|
|
||||||
for _, lconf := range config.Listeners {
|
for _, lconf := range config.Listeners {
|
||||||
if lconf.Net == "" {
|
if lconf.Net == "" {
|
||||||
lconf.Net = "tcp"
|
lconf.Net = "tcp"
|
||||||
|
@ -25,8 +25,8 @@ type Server struct {
|
|||||||
motdFile string
|
motdFile string
|
||||||
name string
|
name string
|
||||||
newConns chan net.Conn
|
newConns chan net.Conn
|
||||||
operators map[string]string
|
operators map[string][]byte
|
||||||
password string
|
password []byte
|
||||||
timeout chan *Client
|
timeout chan *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,19 +34,19 @@ func NewServer(config *Config) *Server {
|
|||||||
server := &Server{
|
server := &Server{
|
||||||
channels: make(ChannelNameMap),
|
channels: make(ChannelNameMap),
|
||||||
clients: make(ClientNameMap),
|
clients: make(ClientNameMap),
|
||||||
commands: make(chan Command, 16),
|
commands: make(chan Command),
|
||||||
ctime: time.Now(),
|
ctime: time.Now(),
|
||||||
idle: make(chan *Client, 16),
|
idle: make(chan *Client),
|
||||||
motdFile: config.MOTD,
|
motdFile: config.MOTD,
|
||||||
name: config.Name,
|
name: config.Name,
|
||||||
newConns: make(chan net.Conn, 16),
|
newConns: make(chan net.Conn),
|
||||||
operators: make(map[string]string),
|
operators: make(map[string][]byte),
|
||||||
password: config.Password,
|
password: config.PasswordBytes(),
|
||||||
timeout: make(chan *Client),
|
timeout: make(chan *Client),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opConf := range config.Operators {
|
for _, opConf := range config.Operators {
|
||||||
server.operators[opConf.Name] = opConf.Password
|
server.operators[opConf.Name] = opConf.PasswordBytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listenerConf := range config.Listeners {
|
for _, listenerConf := range config.Listeners {
|
||||||
@ -106,20 +106,20 @@ func (server *Server) Run() {
|
|||||||
case conn := <-server.newConns:
|
case conn := <-server.newConns:
|
||||||
NewClient(server, conn)
|
NewClient(server, conn)
|
||||||
|
|
||||||
|
case cmd := <-server.commands:
|
||||||
|
server.ProcessCommand(cmd)
|
||||||
|
|
||||||
case client := <-server.idle:
|
case client := <-server.idle:
|
||||||
client.Idle()
|
client.Idle()
|
||||||
|
|
||||||
case client := <-server.timeout:
|
case client := <-server.timeout:
|
||||||
client.Quit("connection timeout")
|
client.Quit("connection timeout")
|
||||||
|
|
||||||
case cmd := <-server.commands:
|
|
||||||
server.ProcessCommand(cmd)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) InitPhase() Phase {
|
func (server *Server) InitPhase() Phase {
|
||||||
if server.password == "" {
|
if server.password == nil {
|
||||||
return Registration
|
return Registration
|
||||||
}
|
}
|
||||||
return Authorization
|
return Authorization
|
||||||
@ -264,10 +264,20 @@ func (msg *CapCommand) HandleAuthServer(server *Server) {
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PassCommand) HandleAuthServer(s *Server) {
|
func (msg *PassCommand) HandleAuthServer(server *Server) {
|
||||||
client := m.Client()
|
client := msg.Client()
|
||||||
|
cmd := &CheckPassCommand{
|
||||||
|
hash: server.password,
|
||||||
|
password: []byte(msg.password),
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
client.checkPass <- cmd
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if s.password != m.password {
|
func (msg *CheckedPassCommand) HandleAuthServer(server *Server) {
|
||||||
|
client := msg.Client()
|
||||||
|
if !msg.isRight {
|
||||||
client.ErrPasswdMismatch()
|
client.ErrPasswdMismatch()
|
||||||
client.Quit("bad password")
|
client.Quit("bad password")
|
||||||
return
|
return
|
||||||
@ -593,13 +603,26 @@ func (msg *WhoCommand) HandleServer(server *Server) {
|
|||||||
func (msg *OperCommand) HandleServer(server *Server) {
|
func (msg *OperCommand) HandleServer(server *Server) {
|
||||||
client := msg.Client()
|
client := msg.Client()
|
||||||
|
|
||||||
if server.operators[msg.name] != msg.password {
|
cmd := &CheckOperCommand{}
|
||||||
|
cmd.hash = server.operators[msg.name]
|
||||||
|
if cmd.hash == nil {
|
||||||
|
client.ErrPasswdMismatch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.password = []byte(msg.password)
|
||||||
|
go func() {
|
||||||
|
client.checkPass <- cmd
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *CheckedOperCommand) HandleServer(server *Server) {
|
||||||
|
client := msg.Client()
|
||||||
|
if !msg.isRight {
|
||||||
client.ErrPasswdMismatch()
|
client.ErrPasswdMismatch()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.flags[Operator] = true
|
client.flags[Operator] = true
|
||||||
|
|
||||||
client.RplYoureOper()
|
client.RplYoureOper()
|
||||||
client.RplUModeIs(client)
|
client.RplUModeIs(client)
|
||||||
}
|
}
|
||||||
|
@ -15,17 +15,15 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Socket struct {
|
type Socket struct {
|
||||||
client *Client
|
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
writer *bufio.Writer
|
writer *bufio.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocket(conn net.Conn, client *Client, commands chan<- Command) *Socket {
|
func NewSocket(conn net.Conn, commands chan<- editableCommand) *Socket {
|
||||||
socket := &Socket{
|
socket := &Socket{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
reader: bufio.NewReader(conn),
|
reader: bufio.NewReader(conn),
|
||||||
client: client,
|
|
||||||
writer: bufio.NewWriter(conn),
|
writer: bufio.NewWriter(conn),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,12 +43,10 @@ func (socket *Socket) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (socket *Socket) readLines(commands chan<- Command) {
|
func (socket *Socket) readLines(commands chan<- editableCommand) {
|
||||||
hostnameLookup := &ProxyCommand{
|
commands <- &ProxyCommand{
|
||||||
hostname: AddrLookupHostname(socket.conn.RemoteAddr()),
|
hostname: AddrLookupHostname(socket.conn.RemoteAddr()),
|
||||||
}
|
}
|
||||||
hostnameLookup.SetClient(socket.client)
|
|
||||||
commands <- hostnameLookup
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
line, err := socket.reader.ReadString('\n')
|
line, err := socket.reader.ReadString('\n')
|
||||||
@ -70,15 +66,12 @@ func (socket *Socket) readLines(commands chan<- Command) {
|
|||||||
// TODO error messaging to client
|
// TODO error messaging to client
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msg.SetClient(socket.client)
|
|
||||||
commands <- msg
|
commands <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &QuitCommand{
|
commands <- &QuitCommand{
|
||||||
message: "connection closed",
|
message: "connection closed",
|
||||||
}
|
}
|
||||||
msg.SetClient(socket.client)
|
|
||||||
commands <- msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (socket *Socket) Write(line string) (err error) {
|
func (socket *Socket) Write(line string) (err error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user