3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +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
import (
"code.google.com/p/go.crypto/bcrypt"
"encoding/base64"
"flag"
"fmt"
"github.com/jlatt/ergonomadic/irc"
"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() {
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 {
log.Fatal(err)
return

View File

@ -1,6 +1,7 @@
package irc
import (
"code.google.com/p/go.crypto/bcrypt"
"fmt"
"log"
"net"
@ -15,6 +16,8 @@ type Client struct {
atime time.Time
awayMessage string
channels ChannelSet
checkPass chan CheckCommand
commands chan editableCommand
ctime time.Time
flags map[UserMode]bool
hasQuit bool
@ -34,19 +37,79 @@ type Client struct {
func NewClient(server *Server, conn net.Conn) *Client {
now := time.Now()
client := &Client{
atime: now,
channels: make(ChannelSet),
ctime: now,
flags: make(map[UserMode]bool),
phase: server.InitPhase(),
server: server,
atime: now,
channels: make(ChannelSet),
checkPass: make(chan CheckCommand),
commands: make(chan editableCommand),
ctime: now,
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)
go client.run()
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
//
@ -106,7 +169,6 @@ func (client *Client) destroy() {
for channel := range client.channels {
channel.Quit(client)
}
client.channels = nil
// clean up server
@ -116,22 +178,16 @@ func (client *Client) destroy() {
if client.loginTimer != nil {
client.loginTimer.Stop()
client.loginTimer = nil
}
if client.idleTimer != nil {
client.idleTimer.Stop()
client.idleTimer = nil
}
if client.quitTimer != nil {
client.quitTimer.Stop()
client.quitTimer = nil
}
client.socket.Close()
client.socket = nil
client.server = nil
if DEBUG_CLIENT {
log.Printf("%s: destroyed", client)
}

View File

@ -1,10 +1,24 @@
package irc
import (
"encoding/base64"
"encoding/json"
"log"
"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 {
Debug map[string]bool
Listeners []ListenerConfig
@ -14,11 +28,19 @@ type Config struct {
Password string
}
func (conf *Config) PasswordBytes() []byte {
return decodePassword(conf.Password)
}
type OperatorConfig struct {
Name string
Password string
}
func (conf *OperatorConfig) PasswordBytes() []byte {
return decodePassword(conf.Password)
}
type ListenerConfig struct {
Net string
Address string
@ -30,10 +52,10 @@ func (config *ListenerConfig) IsTLS() bool {
return (config.Key != "") && (config.Certificate != "")
}
func LoadConfig() (config *Config, err error) {
func LoadConfig(filename string) (config *Config, err error) {
config = &Config{}
file, err := os.Open("ergonomadic.json")
file, err := os.Open(filename)
if err != nil {
return
}
@ -44,6 +66,10 @@ func LoadConfig() (config *Config, err error) {
if err != nil {
return
}
dir := filepath.Dir(filename)
config.MOTD = filepath.Join(dir, config.MOTD)
for _, lconf := range config.Listeners {
if lconf.Net == "" {
lconf.Net = "tcp"

View File

@ -25,8 +25,8 @@ type Server struct {
motdFile string
name string
newConns chan net.Conn
operators map[string]string
password string
operators map[string][]byte
password []byte
timeout chan *Client
}
@ -34,19 +34,19 @@ func NewServer(config *Config) *Server {
server := &Server{
channels: make(ChannelNameMap),
clients: make(ClientNameMap),
commands: make(chan Command, 16),
commands: make(chan Command),
ctime: time.Now(),
idle: make(chan *Client, 16),
idle: make(chan *Client),
motdFile: config.MOTD,
name: config.Name,
newConns: make(chan net.Conn, 16),
operators: make(map[string]string),
password: config.Password,
newConns: make(chan net.Conn),
operators: make(map[string][]byte),
password: config.PasswordBytes(),
timeout: make(chan *Client),
}
for _, opConf := range config.Operators {
server.operators[opConf.Name] = opConf.Password
server.operators[opConf.Name] = opConf.PasswordBytes()
}
for _, listenerConf := range config.Listeners {
@ -106,20 +106,20 @@ func (server *Server) Run() {
case conn := <-server.newConns:
NewClient(server, conn)
case cmd := <-server.commands:
server.ProcessCommand(cmd)
case client := <-server.idle:
client.Idle()
case client := <-server.timeout:
client.Quit("connection timeout")
case cmd := <-server.commands:
server.ProcessCommand(cmd)
}
}
}
func (server *Server) InitPhase() Phase {
if server.password == "" {
if server.password == nil {
return Registration
}
return Authorization
@ -264,10 +264,20 @@ func (msg *CapCommand) HandleAuthServer(server *Server) {
// TODO
}
func (m *PassCommand) HandleAuthServer(s *Server) {
client := m.Client()
func (msg *PassCommand) HandleAuthServer(server *Server) {
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.Quit("bad password")
return
@ -593,13 +603,26 @@ func (msg *WhoCommand) HandleServer(server *Server) {
func (msg *OperCommand) HandleServer(server *Server) {
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()
return
}
client.flags[Operator] = true
client.RplYoureOper()
client.RplUModeIs(client)
}

View File

@ -15,17 +15,15 @@ const (
)
type Socket struct {
client *Client
conn net.Conn
reader *bufio.Reader
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{
conn: conn,
reader: bufio.NewReader(conn),
client: client,
writer: bufio.NewWriter(conn),
}
@ -45,12 +43,10 @@ func (socket *Socket) Close() {
}
}
func (socket *Socket) readLines(commands chan<- Command) {
hostnameLookup := &ProxyCommand{
func (socket *Socket) readLines(commands chan<- editableCommand) {
commands <- &ProxyCommand{
hostname: AddrLookupHostname(socket.conn.RemoteAddr()),
}
hostnameLookup.SetClient(socket.client)
commands <- hostnameLookup
for {
line, err := socket.reader.ReadString('\n')
@ -70,15 +66,12 @@ func (socket *Socket) readLines(commands chan<- Command) {
// TODO error messaging to client
continue
}
msg.SetClient(socket.client)
commands <- msg
}
msg := &QuitCommand{
commands <- &QuitCommand{
message: "connection closed",
}
msg.SetClient(socket.client)
commands <- msg
}
func (socket *Socket) Write(line string) (err error) {