forked from jshiffer/go-xmpp
attempt anonymous only when logging in without JID and password
This commit is contained in:
parent
cc56ae0810
commit
e8c25dcffe
@ -32,7 +32,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *username == "" || *password == "" {
|
if *username == "" || *password == "" {
|
||||||
flag.Usage()
|
if *debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "no username or password were given; attempting ANONYMOUS auth\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*notls {
|
if !*notls {
|
||||||
|
176
xmpp.go
176
xmpp.go
@ -44,6 +44,10 @@ const (
|
|||||||
// Default TLS configuration options
|
// Default TLS configuration options
|
||||||
var DefaultConfig tls.Config
|
var DefaultConfig tls.Config
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DefaultConfig.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
// Cookie is a unique XMPP session identifier
|
// Cookie is a unique XMPP session identifier
|
||||||
type Cookie uint64
|
type Cookie uint64
|
||||||
|
|
||||||
@ -272,11 +276,14 @@ func (c *Client) init(o *Options) error {
|
|||||||
c.p = xml.NewDecoder(c.conn)
|
c.p = xml.NewDecoder(c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var domain string
|
||||||
a := strings.SplitN(o.User, "@", 2)
|
a := strings.SplitN(o.User, "@", 2)
|
||||||
if len(a) != 2 {
|
if len(o.User) > 0 {
|
||||||
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
|
if len(a) != 2 {
|
||||||
}
|
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
|
||||||
domain := a[1]
|
}
|
||||||
|
domain = a[1]
|
||||||
|
} // Otherwise, we'll be attempting ANONYMOUS
|
||||||
|
|
||||||
// Declare intent to be a jabber client and gather stream features.
|
// Declare intent to be a jabber client and gather stream features.
|
||||||
f, err := c.startStream(o, domain)
|
f, err := c.startStream(o, domain)
|
||||||
@ -289,88 +296,101 @@ func (c *Client) init(o *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even digest forms of authentication are unsafe if we do not know that the host
|
if o.User == "" && o.Password == "" {
|
||||||
// we are talking to is the actual server, and not a man in the middle playing
|
found_anonymous := false
|
||||||
// proxy.
|
for _, m := range f.Mechanisms.Mechanism {
|
||||||
if !c.IsEncrypted() && !o.InsecureAllowUnencryptedAuth {
|
if m == "ANONYMOUS" {
|
||||||
return errors.New("refusing to authenticate over unencrypted TCP connection")
|
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
|
||||||
}
|
found_anonymous = true
|
||||||
|
break
|
||||||
mechanism := ""
|
|
||||||
for _, m := range f.Mechanisms.Mechanism {
|
|
||||||
if m == "ANONYMOUS" {
|
|
||||||
mechanism = m
|
|
||||||
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
a := strings.SplitN(o.User, "@", 2)
|
|
||||||
if len(a) != 2 {
|
|
||||||
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
|
|
||||||
}
|
|
||||||
user := a[0]
|
|
||||||
domain := a[1]
|
|
||||||
|
|
||||||
if m == "PLAIN" {
|
|
||||||
mechanism = m
|
|
||||||
// Plain authentication: send base64-encoded \x00 user \x00 password.
|
|
||||||
raw := "\x00" + user + "\x00" + o.Password
|
|
||||||
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
|
||||||
base64.StdEncoding.Encode(enc, []byte(raw))
|
|
||||||
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", nsSASL, enc)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if m == "DIGEST-MD5" {
|
|
||||||
mechanism = m
|
|
||||||
// Digest-MD5 authentication
|
|
||||||
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
|
|
||||||
var ch saslChallenge
|
|
||||||
if err = c.p.DecodeElement(&ch, nil); err != nil {
|
|
||||||
return errors.New("unmarshal <challenge>: " + err.Error())
|
|
||||||
}
|
}
|
||||||
b, err := base64.StdEncoding.DecodeString(string(ch))
|
}
|
||||||
if err != nil {
|
if !found_anonymous {
|
||||||
return err
|
return fmt.Errorf("ANONYMOUS authentication is not an option and username and password were not specified")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Even digest forms of authentication are unsafe if we do not know that the host
|
||||||
|
// we are talking to is the actual server, and not a man in the middle playing
|
||||||
|
// proxy.
|
||||||
|
if !c.IsEncrypted() && !o.InsecureAllowUnencryptedAuth {
|
||||||
|
return errors.New("refusing to authenticate over unencrypted TCP connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
mechanism := ""
|
||||||
|
for _, m := range f.Mechanisms.Mechanism {
|
||||||
|
if m == "ANONYMOUS" {
|
||||||
|
mechanism = m
|
||||||
|
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
tokens := map[string]string{}
|
|
||||||
for _, token := range strings.Split(string(b), ",") {
|
a := strings.SplitN(o.User, "@", 2)
|
||||||
kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
|
if len(a) != 2 {
|
||||||
if len(kv) == 2 {
|
return errors.New("xmpp: invalid username (want user@domain): " + o.User)
|
||||||
if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
|
}
|
||||||
kv[1] = kv[1][1 : len(kv[1])-1]
|
user := a[0]
|
||||||
}
|
domain := a[1]
|
||||||
tokens[kv[0]] = kv[1]
|
|
||||||
|
if m == "PLAIN" {
|
||||||
|
mechanism = m
|
||||||
|
// Plain authentication: send base64-encoded \x00 user \x00 password.
|
||||||
|
raw := "\x00" + user + "\x00" + o.Password
|
||||||
|
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
||||||
|
base64.StdEncoding.Encode(enc, []byte(raw))
|
||||||
|
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", nsSASL, enc)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if m == "DIGEST-MD5" {
|
||||||
|
mechanism = m
|
||||||
|
// Digest-MD5 authentication
|
||||||
|
fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", nsSASL)
|
||||||
|
var ch saslChallenge
|
||||||
|
if err = c.p.DecodeElement(&ch, nil); err != nil {
|
||||||
|
return errors.New("unmarshal <challenge>: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
b, err := base64.StdEncoding.DecodeString(string(ch))
|
||||||
realm, _ := tokens["realm"]
|
if err != nil {
|
||||||
nonce, _ := tokens["nonce"]
|
return err
|
||||||
qop, _ := tokens["qop"]
|
}
|
||||||
charset, _ := tokens["charset"]
|
tokens := map[string]string{}
|
||||||
cnonceStr := cnonce()
|
for _, token := range strings.Split(string(b), ",") {
|
||||||
digestURI := "xmpp/" + domain
|
kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
|
||||||
nonceCount := fmt.Sprintf("%08x", 1)
|
if len(kv) == 2 {
|
||||||
digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
|
if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
|
||||||
message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
|
kv[1] = kv[1][1 : len(kv[1])-1]
|
||||||
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
|
}
|
||||||
|
tokens[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realm, _ := tokens["realm"]
|
||||||
|
nonce, _ := tokens["nonce"]
|
||||||
|
qop, _ := tokens["qop"]
|
||||||
|
charset, _ := tokens["charset"]
|
||||||
|
cnonceStr := cnonce()
|
||||||
|
digestURI := "xmpp/" + domain
|
||||||
|
nonceCount := fmt.Sprintf("%08x", 1)
|
||||||
|
digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
|
||||||
|
message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
|
||||||
|
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
|
||||||
|
|
||||||
fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
|
fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
|
||||||
|
|
||||||
var rspauth saslRspAuth
|
var rspauth saslRspAuth
|
||||||
if err = c.p.DecodeElement(&rspauth, nil); err != nil {
|
if err = c.p.DecodeElement(&rspauth, nil); err != nil {
|
||||||
return errors.New("unmarshal <challenge>: " + err.Error())
|
return errors.New("unmarshal <challenge>: " + err.Error())
|
||||||
|
}
|
||||||
|
b, err = base64.StdEncoding.DecodeString(string(rspauth))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(c.conn, "<response xmlns='%s'/>\n", nsSASL)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
b, err = base64.StdEncoding.DecodeString(string(rspauth))
|
}
|
||||||
if err != nil {
|
if mechanism == "" {
|
||||||
return err
|
return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
|
||||||
}
|
|
||||||
fmt.Fprintf(c.conn, "<response xmlns='%s'/>\n", nsSASL)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mechanism == "" {
|
|
||||||
return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next message should be either success or failure.
|
// Next message should be either success or failure.
|
||||||
name, val, err := next(c.p)
|
name, val, err := next(c.p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user