19896: Use StartTLS + MinTLSVersion regardless of Insecure flag.
[arvados.git] / lib / controller / localdb / login_ldap.go
index 373b113240a042649406949b356d0404d24a4da3..df3982c85f627edc301241777e1b1ae7c1b52105 100644 (file)
@@ -21,12 +21,12 @@ import (
 )
 
 type ldapLoginController struct {
-       Cluster    *arvados.Cluster
-       RailsProxy *railsProxy
+       Cluster *arvados.Cluster
+       Parent  *Conn
 }
 
 func (ctrl *ldapLoginController) Logout(ctx context.Context, opts arvados.LogoutOptions) (arvados.LogoutResponse, error) {
-       return noopLogout(ctrl.Cluster, opts)
+       return logout(ctx, ctrl.Cluster, opts)
 }
 
 func (ctrl *ldapLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
@@ -38,13 +38,34 @@ func (ctrl *ldapLoginController) UserAuthenticate(ctx context.Context, opts arva
        conf := ctrl.Cluster.Login.LDAP
        errFailed := httpserver.ErrorWithStatus(fmt.Errorf("LDAP: Authentication failure (with username %q and password)", opts.Username), http.StatusUnauthorized)
 
+       if conf.SearchAttribute == "" {
+               return arvados.APIClientAuthorization{}, errors.New("config error: SearchAttribute is blank")
+       }
        if opts.Password == "" {
                log.WithField("username", opts.Username).Error("refusing to authenticate with empty password")
                return arvados.APIClientAuthorization{}, errFailed
        }
 
        log = log.WithField("URL", conf.URL.String())
-       l, err := ldap.DialURL(conf.URL.String())
+       var l *ldap.Conn
+       var err error
+       if conf.URL.Scheme == "ldaps" {
+               // ldap.DialURL does not currently allow us to control
+               // tls.Config, so we need to figure out the port
+               // ourselves and call DialTLS.
+               host, port, err := net.SplitHostPort(conf.URL.Host)
+               if err != nil {
+                       // Assume error means no port given
+                       host = conf.URL.Host
+                       port = ldap.DefaultLdapsPort
+               }
+               l, err = ldap.DialTLS("tcp", net.JoinHostPort(host, port), &tls.Config{
+                       ServerName: host,
+                       MinVersion: uint16(conf.MinTLSVersion),
+               })
+       } else {
+               l, err = ldap.DialURL(conf.URL.String())
+       }
        if err != nil {
                log.WithError(err).Error("ldap connection failed")
                return arvados.APIClientAuthorization{}, err
@@ -53,6 +74,7 @@ func (ctrl *ldapLoginController) UserAuthenticate(ctx context.Context, opts arva
 
        if conf.StartTLS {
                var tlsconfig tls.Config
+               tlsconfig.MinVersion = uint16(conf.MinTLSVersion)
                if conf.InsecureTLS {
                        tlsconfig.InsecureSkipVerify = true
                } else {
@@ -89,10 +111,6 @@ func (ctrl *ldapLoginController) UserAuthenticate(ctx context.Context, opts arva
                }
        }
 
-       if conf.SearchAttribute == "" {
-               return arvados.APIClientAuthorization{}, errors.New("config error: must provide SearchAttribute")
-       }
-
        search := fmt.Sprintf("(%s=%s)", ldap.EscapeFilter(conf.SearchAttribute), ldap.EscapeFilter(username))
        if conf.SearchFilters != "" {
                search = fmt.Sprintf("(&%s%s)", conf.SearchFilters, search)
@@ -144,7 +162,7 @@ func (ctrl *ldapLoginController) UserAuthenticate(ctx context.Context, opts arva
                return arvados.APIClientAuthorization{}, errors.New("authentication succeeded but ldap returned no email address")
        }
 
-       return createAPIClientAuthorization(ctx, ctrl.RailsProxy, ctrl.Cluster.SystemRootToken, rpc.UserSessionAuthInfo{
+       return ctrl.Parent.CreateAPIClientAuthorization(ctx, ctrl.Cluster.SystemRootToken, rpc.UserSessionAuthInfo{
                Email:     email,
                FirstName: attrs["givenname"],
                LastName:  attrs["sn"],