3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 11:59:40 +01:00

do bcrypt in the client main routine

This commit is contained in:
Jeremy Latt 2014-02-23 22:21:39 -08:00
parent 2229645a39
commit be089e7f5f
6 changed files with 179 additions and 45 deletions

13
config.json Normal file
View 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 } }

View File

@ -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

View File

@ -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
@ -34,19 +37,79 @@ type Client struct {
func NewClient(server *Server, conn net.Conn) *Client { func NewClient(server *Server, conn net.Conn) *Client {
now := time.Now() now := time.Now()
client := &Client{ client := &Client{
atime: now, atime: now,
channels: make(ChannelSet), channels: make(ChannelSet),
ctime: now, checkPass: make(chan CheckCommand),
flags: make(map[UserMode]bool), commands: make(chan editableCommand),
phase: server.InitPhase(), ctime: now,
server: server, flags: make(map[UserMode]bool),
phase: server.InitPhase(),
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)
} }

View File

@ -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"

View File

@ -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)
} }

View File

@ -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) {