aboutsummaryrefslogtreecommitdiff
path: root/cap_sasl.go
blob: feb1ab41193b9034a152cdc4d0475c405d8d9ec3 (plain) (blame)
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
package main

import (
	"bytes"
	"encoding/base64"
)

type ExtraSasl struct {
	AuthMethod string
}

const (
	RPL_LOGGEDIN    = "900"
	RPL_LOGGEDOUT   = "901"
	ERR_NICKLOCKED  = "902"
	RPL_SASLSUCCESS = "903"
	ERR_SASLFAIL    = "904"
	ERR_SASLTOOLONG = "905"
	ERR_SASLABORTED = "906"
	ERR_SASLALREADY = "907"
	RPL_SASLMECHS   = "908"
)

const (
	panicSaslMethod = "stored illegal SASL method"
)

func init() {
	Caps["sasl"] = "PLAIN,EXTERNAL"
	CommandHandlers["AUTHENTICATE"] = handleClientAuthenticate
}

func handleClientAuthenticate(msg RMsg, client *Client) error {
	_, ok := client.Caps["sasl"]
	if !ok {
		return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed (capability not requested)"))
	}

	if len(msg.Params) < 1 {
		return client.Send(MakeMsg(self, ERR_NEEDMOREPARAMS, "AUTHENTICATE", "Not enough parameters"))
	}

	extraSasl_, ok := client.Extra["sasl"]
	if !ok {
		client.Extra["sasl"] = &ExtraSasl{}
		extraSasl_ = client.Extra["sasl"]
	}
	extraSasl, ok := extraSasl_.(*ExtraSasl)
	if !ok {
		panic(panicType)
	}

	switch extraSasl.AuthMethod {
	case "":
		if msg.Params[0] != "PLAIN" && msg.Params[0] != "EXTERNAL" {
			return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed (invalid method)"))
		}
		extraSasl.AuthMethod = msg.Params[0]
		return client.Send(MakeMsg(self, "AUTHENTICATE", "+"))
	case "*": // Abort
		extraSasl.AuthMethod = ""
		return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed (aborted)"))
	case "EXTERNAL":
		extraSasl.AuthMethod = ""
		return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed"))
	case "PLAIN":
		extraSasl.AuthMethod = ""
		saslPlainData, err := base64.StdEncoding.DecodeString(msg.Params[0])
		if err != nil {
			return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed (base64 decoding error)"))
		}
		saslPlainSegments := bytes.Split(saslPlainData, []byte{0})
		if len(saslPlainSegments) != 3 {
			return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed (not three segments)"))
		}
		_ = string(saslPlainSegments[0]) // authzid unused
		authcid := string(saslPlainSegments[1])
		passwd := string(saslPlainSegments[2])
		if authcid == "runxiyu" && passwd == "hunter2" {
			return client.Send(MakeMsg(self, RPL_SASLSUCCESS, client.Nick, "SASL authentication successful"))
		}
		return client.Send(MakeMsg(self, ERR_SASLFAIL, client.Nick, "SASL authentication failed"))
	default:
		panic(panicSaslMethod)
	}
}