Beispiel #1
0
// TransportForConfig returns a transport for the client, setting the correct
// Proxy, Dial, and TLSClientConfig if needed. It does not mutate c.
// It is the caller's responsibility to then use that transport to set
// the client's httpClient with SetHTTPClient.
func (c *Client) TransportForConfig(tc *TransportConfig) http.RoundTripper {
	if c == nil {
		return nil
	}
	tlsConfig, err := c.TLSConfig()
	if err != nil {
		log.Fatalf("Error while configuring TLS for client: %v", err)
	}
	var transport http.RoundTripper
	proxy := http.ProxyFromEnvironment
	if tc != nil && tc.Proxy != nil {
		proxy = tc.Proxy
	}
	transport = &http.Transport{
		Dial:            c.DialFunc(),
		TLSClientConfig: tlsConfig,
		Proxy:           proxy,
	}
	httpStats := &httputil.StatsTransport{
		Transport: transport,
	}
	if tc != nil {
		httpStats.VerboseLog = tc.Verbose
	}
	transport = httpStats
	if android.OnAndroid() {
		transport = &android.StatsTransport{transport}
	}
	return transport
}
Beispiel #2
0
func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
		if keyId := serverKeyId(); keyId != "" {
			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
			errMsg += hint
		}
		log.Fatal(errMsg)
	}

	conf, err := jsonconfig.ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)
	config = &clientConfig{
		auth:               cfg.OptionalString("auth", ""),
		server:             cfg.OptionalString("server", ""),
		identity:           cfg.OptionalString("identity", ""),
		identitySecretRing: cfg.OptionalString("identitySecretRing", osutil.IdentitySecretRing()),
		trustedCerts:       cfg.OptionalList("trustedCerts"),
		ignoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}
Beispiel #3
0
// SetupAuth sets the client's authMode. It tries from the environment first if we're on android or in dev mode, and then from the client configuration.
func (c *Client) SetupAuth() error {
	// env var takes precedence, but only if we're in dev mode or on android.
	// Too risky otherwise.
	if android.OnAndroid() || os.Getenv("CAMLI_DEV_CAMLI_ROOT") != "" {
		authMode, err := auth.FromEnv()
		if err == nil {
			c.authMode = authMode
			return nil
		}
		if err != auth.ErrNoAuth {
			return fmt.Errorf("Could not set up auth from env var CAMLI_AUTH: %v", err)
		}
	}
	if c.server == "" {
		return fmt.Errorf("No server defined for this client: can not set up auth.")
	}
	authConf := serverAuth(c.server)
	if authConf == "" {
		c.authErr = fmt.Errorf("could not find auth key for server %q in config, defaulting to no auth", c.server)
		c.authMode = auth.None{}
		return nil
	}
	var err error
	c.authMode, err = auth.FromConfig(authConf)
	return err
}
Beispiel #4
0
// DialFunc returns the adequate dial function, depending on
// whether SSL is required, the client's config has some trusted
// certs, and we're on android.
// If the client's config has some trusted certs, the server's
// certificate will be checked against those in the config after
// the TLS handshake.
func (c *Client) DialFunc() func(network, addr string) (net.Conn, error) {
	trustedCerts := c.getTrustedCerts()
	if !c.useTLS() || (!c.InsecureTLS && len(trustedCerts) == 0) {
		// No TLS, or TLS with normal/full verification
		if android.OnAndroid() {
			return func(network, addr string) (net.Conn, error) {
				return android.Dial(network, addr)
			}
		}
		return nil
	}

	return func(network, addr string) (net.Conn, error) {
		var conn *tls.Conn
		var err error
		if android.OnAndroid() {
			con, err := android.Dial(network, addr)
			if err != nil {
				return nil, err
			}
			conn = tls.Client(con, &tls.Config{InsecureSkipVerify: true})
			if err = conn.Handshake(); err != nil {
				return nil, err
			}
		} else {
			conn, err = tls.Dial(network, addr, &tls.Config{InsecureSkipVerify: true})
			if err != nil {
				return nil, err
			}
		}
		if c.InsecureTLS {
			return conn, nil
		}
		certs := conn.ConnectionState().PeerCertificates
		if certs == nil || len(certs) < 1 {
			return nil, errors.New("Could not get server's certificate from the TLS connection.")
		}
		sig := misc.SHA1Prefix(certs[0].Raw)
		for _, v := range trustedCerts {
			if v == sig {
				return conn, nil
			}
		}
		return nil, fmt.Errorf("Server's certificate %v is not in the trusted list", sig)
	}
}
Beispiel #5
0
// TLSConfig returns the correct tls.Config depending on whether
// SSL is required, the client's config has some trusted certs,
// and we're on android.
func (c *Client) TLSConfig() (*tls.Config, error) {
	if !c.useTLS() {
		return nil, nil
	}
	trustedCerts := c.getTrustedCerts()
	if len(trustedCerts) > 0 {
		return &tls.Config{InsecureSkipVerify: true}, nil
	}
	if !android.OnAndroid() {
		return nil, nil
	}
	return android.TLSConfig()
}
Beispiel #6
0
// SecretRingFile returns the filename to the user's GPG secret ring.
// The value comes from either the --secret-keyring flag, the
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
func (c *Client) SecretRingFile() string {
	if secretRing, ok := osutil.ExplicitSecretRingFile(); ok {
		return secretRing
	}
	if android.OnAndroid() {
		panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.")
	}
	configOnce.Do(parseConfig)
	if config.IdentitySecretRing == "" {
		return osutil.SecretRingFile()
	}
	return config.IdentitySecretRing
}
Beispiel #7
0
// initSecretRing sets c.secretRing. It tries, in this order, the --secret-keyring flag,
// the CAMLI_SECRET_RING env var, then defaults to the operating system dependent location
// otherwise.
// It returns an error if the file does not exist.
func (c *initCmd) initSecretRing() error {
	if secretRing, ok := osutil.ExplicitSecretRingFile(); ok {
		c.secretRing = secretRing
	} else {
		if android.OnAndroid() {
			panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.")
		}
		c.secretRing = osutil.SecretRingFile()
	}
	if _, err := os.Stat(c.secretRing); err != nil {
		hint := "\nA GPG key is required, please use 'camput init --newkey'.\n\nOr if you know what you're doing, you can set the global camput flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey=<pubid> or GPGKEY to select which key ID to use."
		return fmt.Errorf("Could not use secret ring file %v: %v.\n%v", c.secretRing, err, hint)
	}
	return nil
}
Beispiel #8
0
func (c *Client) initIgnoredFiles() {
	defer func() {
		c.ignoreChecker = newIgnoreChecker(c.ignoredFiles)
	}()
	if e := os.Getenv("CAMLI_IGNORED_FILES"); e != "" {
		c.ignoredFiles = strings.Split(e, ",")
		return
	}
	c.ignoredFiles = []string{}
	if android.OnAndroid() {
		return
	}
	configOnce.Do(parseConfig)
	c.ignoredFiles = config.IgnoredFiles
}
Beispiel #9
0
// SecretRingFile returns the filename to the user's GPG secret ring.
// The value comes from either a command-line flag, the
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
func (c *Client) SecretRingFile() string {
	if flagSecretRing != "" {
		return flagSecretRing
	}
	if e := os.Getenv("CAMLI_SECRET_RING"); e != "" {
		return e
	}
	if android.OnAndroid() {
		panic("CAMLI_SECRET_RING should have been defined when on android")
	}
	configOnce.Do(parseConfig)
	if config.IdentitySecretRing == "" {
		return osutil.IdentitySecretRing()
	}
	return config.IdentitySecretRing
}
Beispiel #10
0
// SecretRingFile returns the filename to the user's GPG secret ring.
// The value comes from either the --secret-keyring flag, the
// CAMLI_SECRET_RING environment variable, the client config file's
// "identitySecretRing" value, or the operating system default location.
func (c *Client) SecretRingFile() string {
	if secretRing, ok := osutil.ExplicitSecretRingFile(); ok {
		return secretRing
	}
	if android.OnAndroid() {
		panic("on android, so CAMLI_SECRET_RING should have been defined, or --secret-keyring used.")
	}
	if c.paramsOnly {
		log.Print("client: paramsOnly set; cannot get secret ring file from config or env vars.")
		return ""
	}
	if configDisabled {
		panic("Need a secret ring, and config file disabled")
	}
	configOnce.Do(parseConfig)
	if config.IdentitySecretRing == "" {
		return osutil.SecretRingFile()
	}
	return config.IdentitySecretRing
}
Beispiel #11
0
func parseConfig() {
	if android.OnAndroid() {
		return
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
		if keyId := serverKeyId(); keyId != "" {
			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
			errMsg += hint
		}
		log.Fatal(errMsg)
	}

	var err error
	if config, err = jsonconfig.ReadFile(configPath); err != nil {
		log.Fatal(err.Error())
		return
	}
}
Beispiel #12
0
func (c *Client) initTrustedCerts() {
	if e := os.Getenv("CAMLI_TRUSTED_CERT"); e != "" {
		c.trustedCerts = strings.Split(e, ",")
		return
	}
	c.trustedCerts = []string{}
	if android.OnAndroid() {
		return
	}
	if c.server == "" {
		log.Printf("No server defined: can not define trustedCerts for this client.")
		return
	}
	trustedCerts := serverTrustedCerts(c.server)
	if trustedCerts == nil {
		return
	}
	for _, trustedCert := range trustedCerts {
		c.trustedCerts = append(c.trustedCerts, strings.ToLower(trustedCert))
	}
}
Beispiel #13
0
// SetupAuth sets the client's authMode. It tries from the environment first if we're on android or in dev mode, and then from the client configuration.
func (c *Client) SetupAuth() error {
	if c.paramsOnly {
		if c.authMode != nil {
			if _, ok := c.authMode.(*auth.None); !ok {
				return nil
			}
		}
		return errors.New("client: paramsOnly set; auth should not be configured from config or env vars.")
	}
	// env var takes precedence, but only if we're in dev mode or on android.
	// Too risky otherwise.
	if android.OnAndroid() ||
		env.IsDev() ||
		configDisabled {
		authMode, err := auth.FromEnv()
		if err == nil {
			c.authMode = authMode
			return nil
		}
		if err != auth.ErrNoAuth {
			return fmt.Errorf("Could not set up auth from env var CAMLI_AUTH: %v", err)
		}
	}
	if c.server == "" {
		return fmt.Errorf("No server defined for this client: can not set up auth.")
	}
	authConf := serverAuth(c.server)
	if authConf == "" {
		c.authErr = fmt.Errorf("could not find auth key for server %q in config, defaulting to no auth", c.server)
		c.authMode = auth.None{}
		return nil
	}
	var err error
	c.authMode, err = auth.FromConfig(authConf)
	return err
}
Beispiel #14
0
// lazy config parsing when there's a known client already.
// The client c may be nil.
func (c *Client) parseConfig() {
	if android.OnAndroid() {
		panic("parseConfig should never have been called on Android")
	}
	if configDisabled {
		panic("parseConfig should never have been called with CAMLI_DISABLE_CLIENT_CONFIG_FILE set")
	}
	configPath := osutil.UserClientConfigPath()
	if _, err := wkfs.Stat(configPath); os.IsNotExist(err) {
		if c != nil && c.isSharePrefix {
			return
		}
		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
		if keyId := serverKeyId(); keyId != "" {
			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyId, osutil.UserServerConfigPath(), keyId)
			errMsg += hint
		}
		log.Fatal(errMsg)
	}
	// TODO: instead of using jsonconfig, we could read the file, and unmarshall into the structs that we now have in pkg/types/clientconfig. But we'll have to add the old fields (before the name changes, and before the multi-servers change) to the structs as well for our gracefull conversion/error messages to work.
	conf, err := osutil.NewJSONConfigParser().ReadFile(configPath)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := jsonconfig.Obj(conf)

	if singleServerAuth := cfg.OptionalString("auth", ""); singleServerAuth != "" {
		newConf, err := convertToMultiServers(cfg)
		if err != nil {
			log.Print(err)
		} else {
			cfg = newConf
		}
	}

	config = &clientconfig.Config{
		Identity:           cfg.OptionalString("identity", ""),
		IdentitySecretRing: cfg.OptionalString("identitySecretRing", ""),
		IgnoredFiles:       cfg.OptionalList("ignoredFiles"),
	}
	serversList := make(map[string]*clientconfig.Server)
	servers := cfg.OptionalObject("servers")
	for alias, vei := range servers {
		// An alias should never be confused with a host name,
		// so we forbid anything looking like one.
		if isURLOrHostPort(alias) {
			log.Fatalf("Server alias %q looks like a hostname; \".\" or \";\" are not allowed.", alias)
		}
		serverMap, ok := vei.(map[string]interface{})
		if !ok {
			log.Fatalf("entry %q in servers section is a %T, want an object", alias, vei)
		}
		serverConf := jsonconfig.Obj(serverMap)
		server := &clientconfig.Server{
			Server:       cleanServer(serverConf.OptionalString("server", "")),
			Auth:         serverConf.OptionalString("auth", ""),
			IsDefault:    serverConf.OptionalBool("default", false),
			TrustedCerts: serverConf.OptionalList("trustedCerts"),
		}
		if err := serverConf.Validate(); err != nil {
			log.Fatalf("Error in servers section of config file for server %q: %v", alias, err)
		}
		serversList[alias] = server
	}
	config.Servers = serversList
	if err := cfg.Validate(); err != nil {
		printConfigChangeHelp(cfg)
		log.Fatalf("Error in config file: %v", err)
	}
}