Beispiel #1
0
func (c *client) dialServer(server string, useRandomIdentity bool) (*transport.Conn, error) {
	identity := &c.identity
	identityPublic := &c.identityPublic
	if useRandomIdentity {
		var randomIdentity [32]byte
		c.randBytes(randomIdentity[:])

		var randomIdentityPublic [32]byte
		curve25519.ScalarBaseMult(&randomIdentityPublic, &randomIdentity)

		identity = &randomIdentity
		identityPublic = &randomIdentityPublic
	}

	serverIdentity, host, err := parseServer(server, c.testing)
	if err != nil {
		return nil, err
	}
	var tor proxy.Dialer
	if c.testing {
		tor = proxy.Direct
	} else {
		tor = c.torDialer()
	}
	rawConn, err := tor.Dial("tcp", host)
	if err != nil {
		return nil, err
	}
	conn := transport.NewClient(rawConn, identity, identityPublic, serverIdentity)
	if err := conn.Handshake(); err != nil {
		return nil, err
	}
	return conn, nil
}
Beispiel #2
0
func (c *client) dialServer(server string, useRandomIdentity bool) (*transport.Conn, error) {
	identity := &c.identity
	identityPublic := &c.identityPublic
	if useRandomIdentity {
		var randomIdentity [32]byte
		c.randBytes(randomIdentity[:])

		var randomIdentityPublic [32]byte
		curve25519.ScalarBaseMult(&randomIdentityPublic, &randomIdentity)

		identity = &randomIdentity
		identityPublic = &randomIdentityPublic
	}

	serverIdentity, host, err := parseServer(server, c.dev)
	if err != nil {
		return nil, err
	}
	var tor proxy.Dialer
	if c.dev {
		tor = proxy.Direct
	} else {
		tor = c.torDialer()
	}
	rawConn, err := tor.Dial("tcp", host)
	if err != nil {
		return nil, err
	}
	// Sometimes Tor holds the connection open but we never receive
	// anything so we add a 60 second deadline.
	rawConn.SetDeadline(time.Now().Add(60 * time.Second))
	conn := transport.NewClient(rawConn, identity, identityPublic, serverIdentity)
	if err := conn.Handshake(); err != nil {
		return nil, err
	}
	return conn, nil
}
Beispiel #3
0
func main() {
	flag.Parse()

	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err.Error())
	}
	defer terminal.Restore(0, oldState)
	term := terminal.NewTerminal(os.Stdin, "> ")
	updateTerminalSize(term)

	resizeChan := make(chan os.Signal)
	go func() {
		for _ = range resizeChan {
			updateTerminalSize(term)
		}
	}()
	signal.Notify(resizeChan, syscall.SIGWINCH)

	if len(*configFile) == 0 {
		homeDir := os.Getenv("HOME")
		if len(homeDir) == 0 {
			alert(term, "$HOME not set. Please either export $HOME or use the -config-file option.\n")
			return
		}
		persistentDir := filepath.Join(homeDir, "Persistent")
		if stat, err := os.Lstat(persistentDir); err == nil && stat.IsDir() {
			// Looks like Tails.
			homeDir = persistentDir
		}
		*configFile = filepath.Join(homeDir, ".xmpp-client")
	}

	config, err := ParseConfig(*configFile)
	if err != nil {
		alert(term, "Failed to parse config file: "+err.Error())
		config = new(Config)
		if !enroll(config, term) {
			return
		}
		config.filename = *configFile
		config.Save()
	}

	password := config.Password
	if len(password) == 0 {
		if password, err = term.ReadPassword(fmt.Sprintf("Password for %s (will not be saved to disk): ", config.Account)); err != nil {
			alert(term, "Failed to read password: "******"@", 2)
	if len(parts) != 2 {
		alert(term, "invalid username (want user@domain): "+config.Account)
		return
	}
	user := parts[0]
	domain := parts[1]

	var addr string
	addrTrusted := false

	if len(config.Server) > 0 && config.Port > 0 {
		addr = fmt.Sprintf("%s:%d", config.Server, config.Port)
		addrTrusted = true
	} else {
		if len(config.Proxies) > 0 {
			alert(term, "Cannot connect via a proxy without Server and Port being set in the config file as an SRV lookup would leak information.")
			return
		}
		host, port, err := xmpp.Resolve(domain)
		if err != nil {
			alert(term, "Failed to resolve XMPP server: "+err.Error())
			return
		}
		addr = fmt.Sprintf("%s:%d", host, port)
	}

	var dialer proxy.Dialer
	for i := len(config.Proxies) - 1; i >= 0; i-- {
		u, err := url.Parse(config.Proxies[i])
		if err != nil {
			alert(term, "Failed to parse "+config.Proxies[i]+" as a URL: "+err.Error())
			return
		}
		if dialer == nil {
			dialer = proxy.Direct
		}
		if dialer, err = proxy.FromURL(u, dialer); err != nil {
			alert(term, "Failed to parse "+config.Proxies[i]+" as a proxy: "+err.Error())
			return
		}
	}

	var certSHA256 []byte
	if len(config.ServerCertificateSHA256) > 0 {
		certSHA256, err = hex.DecodeString(config.ServerCertificateSHA256)
		if err != nil {
			alert(term, "Failed to parse ServerCertificateSHA256 (should be hex string): "+err.Error())
			return
		}
		if len(certSHA256) != 32 {
			alert(term, "ServerCertificateSHA256 is not 32 bytes long")
			return
		}
	}
	xmppConfig := &xmpp.Config{
		Log:                     &lineLogger{term, nil},
		Create:                  *createAccount,
		TrustedAddress:          addrTrusted,
		Archive:                 false,
		ServerCertificateSHA256: certSHA256,
	}

	if len(config.RawLogFile) > 0 {
		rawLog, err := os.OpenFile(config.RawLogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
		if err != nil {
			alert(term, "Failed to open raw log file: "+err.Error())
			return
		}

		lock := new(sync.Mutex)
		in := rawLogger{
			out:    rawLog,
			prefix: []byte("<- "),
			lock:   lock,
		}
		out := rawLogger{
			out:    rawLog,
			prefix: []byte("-> "),
			lock:   lock,
		}
		in.other, out.other = &out, &in

		xmppConfig.InLog = &in
		xmppConfig.OutLog = &out

		defer in.flush()
		defer out.flush()
	}

	if dialer != nil {
		info(term, "Making connection to "+addr+" via proxy")
		if xmppConfig.Conn, err = dialer.Dial("tcp", addr); err != nil {
			alert(term, "Failed to connect via proxy: "+err.Error())
			return
		}
	}

	conn, err := xmpp.Dial(addr, user, domain, password, xmppConfig)
	if err != nil {
		alert(term, "Failed to connect to XMPP server: "+err.Error())
		return
	}

	s := Session{
		account:           config.Account,
		conn:              conn,
		term:              term,
		conversations:     make(map[string]*otr.Conversation),
		knownStates:       make(map[string]string),
		privateKey:        new(otr.PrivateKey),
		config:            config,
		pendingRosterChan: make(chan *rosterEdit),
		pendingSubscribes: make(map[string]string),
		lastActionTime:    time.Now(),
	}
	info(term, "Fetching roster")

	//var rosterReply chan xmpp.Stanza
	rosterReply, _, err := s.conn.RequestRoster()
	if err != nil {
		alert(term, "Failed to request roster: "+err.Error())
		return
	}

	conn.SignalPresence("")

	s.input = Input{
		term:        term,
		uidComplete: new(priorityList),
	}
	commandChan := make(chan interface{})
	go s.input.ProcessCommands(commandChan)

	stanzaChan := make(chan xmpp.Stanza)
	go s.readMessages(stanzaChan)

	s.privateKey.Parse(config.PrivateKey)
	s.timeouts = make(map[xmpp.Cookie]time.Time)

	info(term, fmt.Sprintf("Your fingerprint is %x", s.privateKey.Fingerprint()))

	ticker := time.NewTicker(1 * time.Second)

MainLoop:
	for {
		select {
		case now := <-ticker.C:
			haveExpired := false
			for _, expiry := range s.timeouts {
				if now.After(expiry) {
					haveExpired = true
					break
				}
			}
			if !haveExpired {
				continue
			}

			newTimeouts := make(map[xmpp.Cookie]time.Time)
			for cookie, expiry := range s.timeouts {
				if now.After(expiry) {
					s.conn.Cancel(cookie)
				} else {
					newTimeouts[cookie] = expiry
				}
			}
			s.timeouts = newTimeouts

		case edit := <-s.pendingRosterChan:
			if !edit.isComplete {
				info(s.term, "Please edit "+edit.fileName+" and run /rostereditdone when complete")
				s.pendingRosterEdit = edit
				continue
			}
			if s.processEditedRoster(edit) {
				s.pendingRosterEdit = nil
			} else {
				alert(s.term, "Please reedit file and run /rostereditdone again")
			}

		case rosterStanza, ok := <-rosterReply:
			if !ok {
				alert(s.term, "Failed to read roster: "+err.Error())
				return
			}
			if s.roster, err = xmpp.ParseRoster(rosterStanza); err != nil {
				alert(s.term, "Failed to parse roster: "+err.Error())
				return
			}
			for _, entry := range s.roster {
				s.input.AddUser(entry.Jid)
			}
			info(s.term, "Roster received")

		case cmd, ok := <-commandChan:
			if !ok {
				warn(term, "Exiting because command channel closed")
				break MainLoop
			}
			s.lastActionTime = time.Now()
			switch cmd := cmd.(type) {
			case quitCommand:
				for to, conversation := range s.conversations {
					msgs := conversation.End()
					for _, msg := range msgs {
						s.conn.Send(to, string(msg))
					}
				}
				break MainLoop
			case versionCommand:
				replyChan, cookie, err := s.conn.SendIQ(cmd.User, "get", xmpp.VersionQuery{})
				if err != nil {
					alert(s.term, "Error sending version request: "+err.Error())
					continue
				}
				s.timeouts[cookie] = time.Now().Add(5 * time.Second)
				go s.awaitVersionReply(replyChan, cmd.User)
			case rosterCommand:
				info(s.term, "Current roster:")
				maxLen := 0
				for _, item := range s.roster {
					if maxLen < len(item.Jid) {
						maxLen = len(item.Jid)
					}
				}

				for _, item := range s.roster {
					state, ok := s.knownStates[item.Jid]

					line := ""
					if ok {
						line += "[*] "
					} else if cmd.OnlineOnly {
						continue
					} else {
						line += "[ ] "
					}

					line += item.Jid
					numSpaces := 1 + (maxLen - len(item.Jid))
					for i := 0; i < numSpaces; i++ {
						line += " "
					}
					line += item.Subscription + "\t" + item.Name
					if ok {
						line += "\t" + state
					}
					info(s.term, line)
				}
			case rosterEditCommand:
				if s.pendingRosterEdit != nil {
					warn(s.term, "Aborting previous roster edit")
					s.pendingRosterEdit = nil
				}
				rosterCopy := make([]xmpp.RosterEntry, len(s.roster))
				copy(rosterCopy, s.roster)
				go s.editRoster(rosterCopy)
			case rosterEditDoneCommand:
				if s.pendingRosterEdit == nil {
					warn(s.term, "No roster edit in progress. Use /rosteredit to start one")
					continue
				}
				go s.loadEditedRoster(*s.pendingRosterEdit)
			case toggleStatusUpdatesCommand:
				s.config.HideStatusUpdates = !s.config.HideStatusUpdates
				s.config.Save()
				// Tell the user the current state of the statuses
				if s.config.HideStatusUpdates {
					info(s.term, "Status updated disabled")
				} else {
					info(s.term, "Status updates enabled")
				}
			case confirmCommand:
				s.handleConfirmOrDeny(cmd.User, true /* confirm */)
			case denyCommand:
				s.handleConfirmOrDeny(cmd.User, false /* deny */)
			case addCommand:
				s.conn.SendPresence(cmd.User, "subscribe", "" /* generate id */)
			case msgCommand:
				conversation, ok := s.conversations[cmd.to]
				if (!ok || !conversation.IsEncrypted()) && config.ShouldEncryptTo(cmd.to) {
					warn(s.term, fmt.Sprintf("Did not send: no encryption established with %s", cmd.to))
					continue
				}
				var msgs [][]byte
				message := []byte(cmd.msg)
				// Automatically tag all outgoing plaintext
				// messages with a whitespace tag that
				// indicates that we support OTR.
				if config.OTRAutoAppendTag &&
					!bytes.Contains(message, []byte("?OTR")) &&
					(!ok || !conversation.IsEncrypted()) {
					message = append(message, OTRWhitespaceTag...)
				}
				if ok {
					var err error
					msgs, err = conversation.Send(message)
					if err != nil {
						alert(s.term, err.Error())
						break
					}
				} else {
					msgs = [][]byte{[]byte(message)}
				}
				for _, message := range msgs {
					s.conn.Send(cmd.to, string(message))
				}
			case otrCommand:
				s.conn.Send(string(cmd.User), otr.QueryMessage)
			case otrInfoCommand:
				info(term, fmt.Sprintf("Your OTR fingerprint is %x", s.privateKey.Fingerprint()))
				for to, conversation := range s.conversations {
					if conversation.IsEncrypted() {
						info(s.term, fmt.Sprintf("Secure session with %s underway:", to))
						printConversationInfo(s, to, conversation)
					}
				}
			case endOTRCommand:
				to := string(cmd.User)
				conversation, ok := s.conversations[to]
				if !ok {
					alert(s.term, "No secure session established")
					break
				}
				msgs := conversation.End()
				for _, msg := range msgs {
					s.conn.Send(to, string(msg))
				}
			case authQACommand:
				to := string(cmd.User)
				conversation, ok := s.conversations[to]
				if !ok {
					alert(s.term, "Can't authenticate without a secure conversation established")
					break
				}
				msgs, err := conversation.Authenticate(cmd.Question, []byte(cmd.Secret))
				if err != nil {
					alert(s.term, "Error while starting authentication with "+to+": "+err.Error())
				}
				for _, msg := range msgs {
					s.conn.Send(to, string(msg))
				}
			case authOobCommand:
				fpr, err := hex.DecodeString(cmd.Fingerprint)
				if err != nil {
					alert(s.term, fmt.Sprintf("Invalid fingerprint %s - not authenticated", cmd.Fingerprint))
					break
				}
				existing := s.config.UserIdForFingerprint(fpr)
				if len(existing) != 0 {
					alert(s.term, fmt.Sprintf("Fingerprint %s already belongs to %s", cmd.Fingerprint, existing))
					break
				}
				s.config.KnownFingerprints = append(s.config.KnownFingerprints, KnownFingerprint{fingerprint: fpr, UserId: cmd.User})
				s.config.Save()
				info(s.term, fmt.Sprintf("Saved manually verified fingerprint %s for %s", cmd.Fingerprint, cmd.User))
			case awayCommand:
				s.conn.SignalPresence("away")
			case chatCommand:
				s.conn.SignalPresence("chat")
			case dndCommand:
				s.conn.SignalPresence("dnd")
			case xaCommand:
				s.conn.SignalPresence("xa")
			case onlineCommand:
				s.conn.SignalPresence("")
			}
		case rawStanza, ok := <-stanzaChan:
			if !ok {
				warn(term, "Exiting because channel to server closed")
				break MainLoop
			}
			switch stanza := rawStanza.Value.(type) {
			case *xmpp.ClientMessage:
				s.processClientMessage(stanza)
			case *xmpp.ClientPresence:
				s.processPresence(stanza)
			case *xmpp.ClientIQ:
				if stanza.Type != "get" && stanza.Type != "set" {
					continue
				}
				reply := s.processIQ(stanza)
				if reply == nil {
					reply = xmpp.ErrorReply{
						Type:  "cancel",
						Error: xmpp.ErrorBadRequest{},
					}
				}
				if err := s.conn.SendIQReply(stanza.From, "result", stanza.Id, reply); err != nil {
					alert(term, "Failed to send IQ message: "+err.Error())
				}
			default:
				info(term, fmt.Sprintf("%s %s", rawStanza.Name, rawStanza.Value))
			}
		}
	}

	os.Stdout.Write([]byte("\n"))
}
Beispiel #4
0
func main() {
	runtime.GOMAXPROCS(2)
	flag.Parse()

	if len(*secret) == 0 {
		fmt.Fprintf(os.Stderr, "The shared secret must be given as --secret\n")
		os.Exit(2)
	}
	if len(*keyFile) == 0 {
		fmt.Fprintf(os.Stderr, "The path to a find containing the public key material to exchange must be given as --key-file\n")
		os.Exit(2)
	}

	pubKeyBytes, err := ioutil.ReadFile(*keyFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read key file: %s\n", err)
		os.Exit(2)
	}

	const maxBody = 4096
	if limit := maxBody - 24 - secretbox.Overhead; len(pubKeyBytes) > limit {
		fmt.Fprintf(os.Stderr, "--key-file is too large (%d bytes of a maximum of %d)\n", len(pubKeyBytes), limit)
		os.Exit(2)
	}

	// Run scrypt on a goroutine so that we can overlap it with the network
	// delay.
	keyChan := make(chan []byte)
	go deriveKey(keyChan, *secret)

	var dialer proxy.Dialer
	dialer = proxy.Direct
	if len(*torProxy) > 0 {
		fmt.Fprintf(os.Stderr, "Using SOCKS5 proxy at %s\n", *torProxy)
		dialer, err = proxy.SOCKS5("tcp", *torProxy, nil, dialer)
		if err != nil {
			panic(err)
		}
	}

	fmt.Fprintf(os.Stderr, "Starting TCP connection to %s\n", *server)
	rawConn, err := dialer.Dial("tcp", *server)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to connect to server: %s\n", err)
		os.Exit(2)
	}

	var conn net.Conn
	if *useTLS {
		hostname, _, err := net.SplitHostPort(*server)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to split %s into host and post: %s\n", *server, err)
			os.Exit(2)
		}
		tlsConn := tls.Client(rawConn, &tls.Config{
			ServerName: hostname,
		})
		fmt.Fprintf(os.Stderr, "Starting TLS handshake\n")
		if err := tlsConn.Handshake(); err != nil {
			rawConn.Close()
			fmt.Fprintf(os.Stderr, "TLS handshake failed: %s\n", err)
			os.Exit(2)
		}
		conn = tlsConn
	} else {
		conn = rawConn
	}
	defer conn.Close()

	var keySlice []byte
	select {
	case keySlice = <-keyChan:
	default:
		fmt.Fprintf(os.Stderr, "Waiting for key derivation to complete. This may take some time.\n")
		keySlice = <-keyChan
	}
	var key [32]byte
	copy(key[:], keySlice)

	mac := hmac.New(sha256.New, key[:])
	mac.Write(pubKeyBytes)
	nonceSlice := mac.Sum(nil)
	var nonce [24]byte
	copy(nonce[:], nonceSlice)

	box := make([]byte, len(nonce)+secretbox.Overhead+len(pubKeyBytes))
	copy(box, nonce[:])
	secretbox.Seal(box[len(nonce):len(nonce)], pubKeyBytes, &nonce, &key)

	h := sha256.New()
	h.Write(key[:])
	tag := h.Sum(nil)

	body := bytes.NewReader(box)
	request, err := http.NewRequest("POST", "http://"+*server+"/exchange/"+hex.EncodeToString(tag), body)
	if err != nil {
		panic(err)
	}
	request.ContentLength = int64(len(box))
	request.Header.Add("Content-Type", "application/octet-stream")

	request.Write(conn)
	fmt.Fprintf(os.Stderr, "Request sent. Waiting for HTTP reply\n")
	replyReader := bufio.NewReader(conn)

	response, err := http.ReadResponse(replyReader, request)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading reply from server: %s\n", err)
		os.Exit(2)
	}

	switch response.StatusCode {
	case 409:
		fmt.Fprintf(os.Stderr, "The transaction failed because this key has been used recently by two other parties.\n")
		os.Exit(2)
	case 204:
		fmt.Fprintf(os.Stderr, "Material submitted, but the other party has yet to start the process. Try again later with the same arguments.\n")
		os.Exit(1)
	case 200:
		r := &io.LimitedReader{R: response.Body, N: maxBody + 1}
		body, err := ioutil.ReadAll(r)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading from server: %s\n", err)
			os.Exit(2)
		}
		if len(body) > maxBody {
			fmt.Fprintf(os.Stderr, "Reply from server is too large\n")
			os.Exit(2)
		}
		if len(body) < len(nonce)+secretbox.Overhead {
			fmt.Fprintf(os.Stderr, "Reply from server is too short to be valid: %x\n", body)
			os.Exit(2)
		}
		copy(nonce[:], body)
		unsealed, ok := secretbox.Open(nil, body[len(nonce):], &nonce, &key)
		if !ok {
			fmt.Fprintf(os.Stderr, "Failed to authenticate reply from server\n")
			os.Exit(2)
		}
		os.Stdout.Write(unsealed)
	default:
		fmt.Fprintf(os.Stderr, "HTTP error from server: %s\n", response.Status)
		io.Copy(os.Stderr, response.Body)
		os.Exit(2)
	}
}
Beispiel #5
0
func main() {
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 0 {
		usage()
	}

	// Setup the TLS client configuration.
	config := &tls.Config{ServerName: *host}
	if *skipVerify {
		if *verbose {
			log.Print("WARNING: Skipping certificate verification")
		}
		config.InsecureSkipVerify = true
	}
	if *certFile != "" {
		data, err := ioutil.ReadFile(*certFile)
		if err != nil {
			log.Fatalf("could not read certificate file: %s", err)
		}
		p := x509.NewCertPool()
		if ok := p.AppendCertsFromPEM(data); !ok {
			log.Fatalf("could not parse certificates in %q", *certFile)
		}
		config.RootCAs = p
	}

	// Determine how we should converse with the remote server.
	fn, ok := tlsMethods[*method]
	if !ok {
		log.Fatalf("unknown connection method %q", method)
	}

	// Determine the proxy dialer method.
	var dialer proxy.Dialer = proxy.Direct
	if *proxyURL != "" {
		u, err := url.Parse(*proxyURL)
		if err != nil {
			log.Fatalf("failed to parse proxy url: %s", err)
		}
		d, err := proxy.FromURL(u, dialer)
		if err != nil {
			log.Fatal(err)
		}
		dialer = d
		usualHandshakeRTT = proxyHandshakeRTT
	}

	// Connect to the remote server.
	c, err := dialer.Dial("tcp", net.JoinHostPort(*host, *port))
	if err != nil {
		log.Fatalf("could not dial remote host: %s", err)
	}

	// Wrap the connection in a shim so we can sniff the TLS traffic.
	s := &shim{Conn: c}

	// Run our predetermined TLS conversation.
	// If successful, the connection will be automatically closed.
	if err := fn(s, config); err != nil {
		s.Close()
		log.Fatalf("error in TLS conversation: %s", err)
	}

	// Warn if the TLS handshake took too long.
	rtt := s.HandshakeRTT()
	if rtt > usualHandshakeRTT {
		log.Printf("WARNING: the TLS handshake took too long (%s)\n", rtt)
	}

	// Show the server's time in Unix format.
	if *showTime {
		fmt.Println(s.ServerTime().Format(time.UnixDate))
	}

	// Go no further if we don't have to set the clock.
	if *dontSetClock {
		return
	}

	// Check for unreasonable time values.
	t := s.ServerTime().Add(rtt)
	if t.After(maxReasonableTime) {
		log.Fatal("remote time is too far in the future")
	}
	if t.Before(recentCompileTime) {
		log.Fatal("remote time is too far in the past")
	}

	// Set the system clock.
	if err := setClock(t); err != nil {
		log.Fatal(err)
	}
}