Exemplo n.º 1
0
// AuthKey authenticates based on a public key.
//
// Params:
// 	- metadata (ssh.ConnMetadata)
// 	- key (ssh.PublicKey)
// 	- authorizedKeys ([]string): List of lines from an authorized keys file.
//
// Returns:
// 	*ssh.Permissions
//
func AuthKey(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	meta := p.Get("metadata", nil).(ssh.ConnMetadata)
	key := p.Get("key", nil).(ssh.PublicKey)
	authorized := p.Get("authorizedKeys", []string{}).([]string)

	auth := new(ssh.CertChecker)
	auth.UserKeyFallback = func(meta ssh.ConnMetadata, pk ssh.PublicKey) (*ssh.Permissions, error) {

		// This gives us a string in the form "ssh-rsa LONG_KEY"
		suppliedType := key.Type()
		supplied := key.Marshal()

		for _, allowedKey := range authorized {
			allowed, _, _, _, err := ssh.ParseAuthorizedKey([]byte(allowedKey))
			if err != nil {
				log.Infof(c, "Could not parse authorized key '%q': %s", allowedKey, err)
				continue
			}

			// We use a contstant time compare more as a precaution than anything
			// else. A timing attack here would be very difficult, but... better
			// safe than sorry.
			if allowed.Type() == suppliedType && subtle.ConstantTimeCompare(allowed.Marshal(), supplied) == 1 {
				log.Infof(c, "Key accepted for user %s.", meta.User())
				perm := &ssh.Permissions{
					Extensions: map[string]string{
						"user": meta.User(),
					},
				}
				return perm, nil
			}
		}

		return nil, fmt.Errorf("No matching keys found.")
	}

	return auth.Authenticate(meta, key)
}