26 } |
26 } |
27 |
27 |
28 if digestMd5 { |
28 if digestMd5 { |
29 auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: "auth"}, |
29 auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: "auth"}, |
30 Mechanism: "DIGEST-MD5"} |
30 Mechanism: "DIGEST-MD5"} |
31 cl.sendXml <- auth |
31 cl.sendRaw <- auth |
32 } |
32 } |
33 } |
33 } |
34 |
34 |
35 // Server is responding to our auth request. |
35 // Server is responding to our auth request. |
36 func (cl *Client) handleSasl(srv *auth) { |
36 func (cl *Client) handleSasl(srv *auth) { |
37 switch strings.ToLower(srv.XMLName.Local) { |
37 switch strings.ToLower(srv.XMLName.Local) { |
38 case "challenge": |
38 case "challenge": |
39 b64 := base64.StdEncoding |
39 b64 := base64.StdEncoding |
40 str, err := b64.DecodeString(srv.Chardata) |
40 str, err := b64.DecodeString(srv.Chardata) |
41 if err != nil { |
41 if err != nil { |
42 Warn.Logf("SASL challenge decode: %s", err) |
42 cl.setError(fmt.Errorf("SASL: %v", err)) |
43 return |
43 return |
44 } |
44 } |
45 srvMap := parseSasl(string(str)) |
45 srvMap := parseSasl(string(str)) |
46 |
46 |
47 if cl.saslExpected == "" { |
47 if cl.saslExpected == "" { |
48 cl.saslDigest1(srvMap) |
48 cl.saslDigest1(srvMap) |
49 } else { |
49 } else { |
50 cl.saslDigest2(srvMap) |
50 cl.saslDigest2(srvMap) |
51 } |
51 } |
52 case "failure": |
52 case "failure": |
53 Info.Log("SASL authentication failed") |
53 cl.setError(fmt.Errorf("SASL authentication failed")) |
54 case "success": |
54 case "success": |
55 cl.setStatus(StatusAuthenticated) |
55 cl.setStatus(StatusAuthenticated) |
56 Info.Log("Sasl authentication succeeded") |
|
57 cl.Features = nil |
56 cl.Features = nil |
58 ss := &stream{To: cl.Jid.Domain, Version: XMPPVersion} |
57 ss := &stream{To: cl.Jid.Domain(), Version: XMPPVersion} |
59 cl.sendXml <- ss |
58 cl.sendRaw <- ss |
60 } |
59 } |
61 } |
60 } |
62 |
61 |
63 func (cl *Client) saslDigest1(srvMap map[string]string) { |
62 func (cl *Client) saslDigest1(srvMap map[string]string) { |
64 // Make sure it supports qop=auth |
63 // Make sure it supports qop=auth |
79 realm = strings.Fields(srvMap["realm"])[0] |
78 realm = strings.Fields(srvMap["realm"])[0] |
80 } |
79 } |
81 |
80 |
82 passwd := cl.password |
81 passwd := cl.password |
83 nonce := srvMap["nonce"] |
82 nonce := srvMap["nonce"] |
84 digestUri := "xmpp/" + cl.Jid.Domain |
83 digestUri := "xmpp/" + cl.Jid.Domain() |
85 nonceCount := int32(1) |
84 nonceCount := int32(1) |
86 nonceCountStr := fmt.Sprintf("%08x", nonceCount) |
85 nonceCountStr := fmt.Sprintf("%08x", nonceCount) |
87 |
86 |
88 // Begin building the response. Username is |
87 // Begin building the response. Username is |
89 // user@domain or just domain. |
88 // user@domain or just domain. |
90 var username string |
89 var username string |
91 if cl.Jid.Node == "" { |
90 if cl.Jid.Node() == "" { |
92 username = cl.Jid.Domain |
91 username = cl.Jid.Domain() |
93 } else { |
92 } else { |
94 username = cl.Jid.Node |
93 username = cl.Jid.Node() |
95 } |
94 } |
96 |
95 |
97 // Generate our own nonce from random data. |
96 // Generate our own nonce from random data. |
98 randSize := big.NewInt(0) |
97 randSize := big.NewInt(0) |
99 randSize.Lsh(big.NewInt(1), 64) |
98 randSize.Lsh(big.NewInt(1), 64) |
100 cnonce, err := rand.Int(rand.Reader, randSize) |
99 cnonce, err := rand.Int(rand.Reader, randSize) |
101 if err != nil { |
100 if err != nil { |
102 Warn.Logf("SASL rand: %s", err) |
101 cl.setError(fmt.Errorf("SASL rand: %v", err)) |
103 return |
102 return |
104 } |
103 } |
105 cnonceStr := fmt.Sprintf("%016x", cnonce) |
104 cnonceStr := fmt.Sprintf("%016x", cnonce) |
106 |
105 |
107 /* Now encode the actual password response, as well as the |
106 /* Now encode the actual password response, as well as the |
129 // Encode the map and send it. |
128 // Encode the map and send it. |
130 clStr := packSasl(clMap) |
129 clStr := packSasl(clMap) |
131 b64 := base64.StdEncoding |
130 b64 := base64.StdEncoding |
132 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}, |
131 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}, |
133 Chardata: b64.EncodeToString([]byte(clStr))} |
132 Chardata: b64.EncodeToString([]byte(clStr))} |
134 cl.sendXml <- clObj |
133 cl.sendRaw <- clObj |
135 } |
134 } |
136 |
135 |
137 func (cl *Client) saslDigest2(srvMap map[string]string) { |
136 func (cl *Client) saslDigest2(srvMap map[string]string) { |
138 if cl.saslExpected == srvMap["rspauth"] { |
137 if cl.saslExpected == srvMap["rspauth"] { |
139 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}} |
138 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}} |
140 cl.sendXml <- clObj |
139 cl.sendRaw <- clObj |
141 } else { |
140 } else { |
142 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "failure"}, Any: &Generic{XMLName: xml.Name{Space: NsSASL, |
141 clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "failure"}, Any: &Generic{XMLName: xml.Name{Space: NsSASL, |
143 Local: "abort"}}} |
142 Local: "abort"}}} |
144 cl.sendXml <- clObj |
143 cl.sendRaw <- clObj |
145 } |
144 } |
146 } |
145 } |
147 |
146 |
148 // Takes a string like `key1=value1,key2="value2"...` and returns a |
147 // Takes a string like `key1=value1,key2="value2"...` and returns a |
149 // key/value map. |
148 // key/value map. |