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:
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
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user