xmpp/sasl.go
changeset 183 b4bd77d58a3e
parent 178 ccfebbd9f49b
child 184 ce49140fe60b
equal deleted inserted replaced
182:626c390682fc 183:b4bd77d58a3e
    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
    67 		if qop == "auth" {
    66 		if qop == "auth" {
    68 			hasAuth = true
    67 			hasAuth = true
    69 		}
    68 		}
    70 	}
    69 	}
    71 	if !hasAuth {
    70 	if !hasAuth {
    72 		Warn.Log("Server doesn't support SASL auth")
    71 		cl.setError(fmt.Errorf("Server doesn't support SASL auth"))
    73 		return
    72 		return
    74 	}
    73 	}
    75 
    74 
    76 	// Pick a realm.
    75 	// Pick a realm.
    77 	var realm string
    76 	var realm string
    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.