| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	jwtgo "github.com/dgrijalva/jwt-go" | 
					
						
							|  |  |  | 	jwtreq "github.com/dgrijalva/jwt-go/request" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	jwtAlgorithm = "Bearer" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Default JWT token for web handlers is one day.
 | 
					
						
							|  |  |  | 	defaultJWTExpiry = 24 * time.Hour | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Inter-node JWT token expiry is 100 years approx.
 | 
					
						
							|  |  |  | 	defaultInterNodeJWTExpiry = 100 * 365 * 24 * time.Hour | 
					
						
							| 
									
										
										
										
											2017-07-25 03:46:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// URL JWT token expiry is one minute (might be exposed).
 | 
					
						
							|  |  |  | 	defaultURLJWTExpiry = time.Minute | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:43 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	errInvalidAccessKeyID   = errors.New("The access key ID you provided does not exist in our records") | 
					
						
							|  |  |  | 	errChangeCredNotAllowed = errors.New("Changing access key and secret key not allowed") | 
					
						
							|  |  |  | 	errAuthentication       = errors.New("Authentication failed, check your access credentials") | 
					
						
							|  |  |  | 	errNoAuthToken          = errors.New("JWT token missing") | 
					
						
							| 
									
										
										
										
											2019-05-30 04:18:46 +08:00
										 |  |  | 	errIncorrectCreds       = errors.New("Current access key or secret key is incorrect") | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:43 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 09:44:58 +08:00
										 |  |  | func authenticateJWTUsers(accessKey, secretKey string, expiry time.Duration) (string, error) { | 
					
						
							|  |  |  | 	passedCredential, err := auth.CreateCredentials(accessKey, secretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	serverCred := globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 	if serverCred.AccessKey != passedCredential.AccessKey { | 
					
						
							|  |  |  | 		var ok bool | 
					
						
							|  |  |  | 		serverCred, ok = globalIAMSys.GetUser(accessKey) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return "", errInvalidAccessKeyID | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !serverCred.Equal(passedCredential) { | 
					
						
							|  |  |  | 		return "", errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{ | 
					
						
							|  |  |  | 		ExpiresAt: UTCNow().Add(expiry).Unix(), | 
					
						
							|  |  |  | 		Subject:   accessKey, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return jwt.SignedString([]byte(serverCred.SecretKey)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func authenticateJWTAdmin(accessKey, secretKey string, expiry time.Duration) (string, error) { | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 	passedCredential, err := auth.CreateCredentials(accessKey, secretKey) | 
					
						
							| 
									
										
										
										
											2017-03-16 15:16:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	serverCred := globalServerConfig.GetCredential() | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 15:16:06 +08:00
										 |  |  | 	if serverCred.AccessKey != passedCredential.AccessKey { | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 		return "", errInvalidAccessKeyID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 15:16:06 +08:00
										 |  |  | 	if !serverCred.Equal(passedCredential) { | 
					
						
							|  |  |  | 		return "", errAuthentication | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 02:34:00 +08:00
										 |  |  | 	jwt := jwtgo.NewWithClaims(jwtgo.SigningMethodHS512, jwtgo.StandardClaims{ | 
					
						
							|  |  |  | 		ExpiresAt: UTCNow().Add(expiry).Unix(), | 
					
						
							| 
									
										
										
										
											2017-09-20 03:37:56 +08:00
										 |  |  | 		Subject:   accessKey, | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2018-01-11 02:34:00 +08:00
										 |  |  | 	return jwt.SignedString([]byte(serverCred.SecretKey)) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func authenticateNode(accessKey, secretKey string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-10-17 09:44:58 +08:00
										 |  |  | 	return authenticateJWTAdmin(accessKey, secretKey, defaultInterNodeJWTExpiry) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func authenticateWeb(accessKey, secretKey string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-10-17 09:44:58 +08:00
										 |  |  | 	return authenticateJWTUsers(accessKey, secretKey, defaultJWTExpiry) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-25 03:46:37 +08:00
										 |  |  | func authenticateURL(accessKey, secretKey string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-10-17 09:44:58 +08:00
										 |  |  | 	return authenticateJWTUsers(accessKey, secretKey, defaultURLJWTExpiry) | 
					
						
							| 
									
										
										
										
											2017-07-25 03:46:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | // Callback function used for parsing
 | 
					
						
							|  |  |  | func webTokenCallback(jwtToken *jwtgo.Token) (interface{}, error) { | 
					
						
							|  |  |  | 	if _, ok := jwtToken.Method.(*jwtgo.SigningMethodHMAC); !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Unexpected signing method: %v", jwtToken.Header["alg"]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := jwtToken.Claims.Valid(); err != nil { | 
					
						
							|  |  |  | 		return nil, errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if claims, ok := jwtToken.Claims.(*jwtgo.StandardClaims); ok { | 
					
						
							|  |  |  | 		if claims.Subject == globalServerConfig.GetCredential().AccessKey { | 
					
						
							|  |  |  | 			return []byte(globalServerConfig.GetCredential().SecretKey), nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if globalIAMSys == nil { | 
					
						
							|  |  |  | 			return nil, errInvalidAccessKeyID | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cred, ok := globalIAMSys.GetUser(claims.Subject) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return nil, errInvalidAccessKeyID | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return []byte(cred.SecretKey), nil | 
					
						
							| 
									
										
										
										
											2017-10-18 13:44:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil, errAuthentication | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseJWTWithClaims(tokenString string, claims jwtgo.Claims) (*jwtgo.Token, error) { | 
					
						
							|  |  |  | 	p := &jwtgo.Parser{ | 
					
						
							|  |  |  | 		SkipClaimsValidation: true, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	jwtToken, err := p.ParseWithClaims(tokenString, claims, webTokenCallback) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		switch e := err.(type) { | 
					
						
							|  |  |  | 		case *jwtgo.ValidationError: | 
					
						
							|  |  |  | 			if e.Inner == nil { | 
					
						
							|  |  |  | 				return nil, errAuthentication | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil, e.Inner | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, errAuthentication | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	return jwtToken, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func isAuthTokenValid(token string) bool { | 
					
						
							|  |  |  | 	_, _, err := webTokenAuthenticate(token) | 
					
						
							|  |  |  | 	return err == nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func webTokenAuthenticate(token string) (jwtgo.StandardClaims, bool, error) { | 
					
						
							|  |  |  | 	var claims = jwtgo.StandardClaims{} | 
					
						
							|  |  |  | 	if token == "" { | 
					
						
							|  |  |  | 		return claims, false, errNoAuthToken | 
					
						
							| 
									
										
										
										
											2017-09-20 03:37:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	jwtToken, err := parseJWTWithClaims(token, &claims) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return claims, false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !jwtToken.Valid { | 
					
						
							|  |  |  | 		return claims, false, errAuthentication | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	owner := claims.Subject == globalServerConfig.GetCredential().AccessKey | 
					
						
							|  |  |  | 	return claims, owner, nil | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:42 +08:00
										 |  |  | // Check if the request is authenticated.
 | 
					
						
							|  |  |  | // Returns nil if the request is authenticated. errNoAuthToken if token missing.
 | 
					
						
							|  |  |  | // Returns errAuthentication for all other errors.
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | func webRequestAuthenticate(req *http.Request) (jwtgo.StandardClaims, bool, error) { | 
					
						
							|  |  |  | 	var claims = jwtgo.StandardClaims{} | 
					
						
							|  |  |  | 	tokStr, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(req) | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:42 +08:00
										 |  |  | 		if err == jwtreq.ErrNoTokenInRequest { | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 			return claims, false, errNoAuthToken | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return claims, false, err | 
					
						
							| 
									
										
										
										
											2017-09-20 03:37:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	jwtToken, err := parseJWTWithClaims(tokStr, &claims) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return claims, false, err | 
					
						
							| 
									
										
										
										
											2017-09-20 03:37:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:42 +08:00
										 |  |  | 	if !jwtToken.Valid { | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return claims, false, errAuthentication | 
					
						
							| 
									
										
										
										
											2017-01-12 05:26:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	owner := claims.Subject == globalServerConfig.GetCredential().AccessKey | 
					
						
							|  |  |  | 	return claims, owner, nil | 
					
						
							| 
									
										
										
										
											2016-12-28 00:28:10 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func newAuthToken() string { | 
					
						
							|  |  |  | 	cred := globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 	token, err := authenticateNode(cred.AccessKey, cred.SecretKey) | 
					
						
							|  |  |  | 	logger.CriticalIf(context.Background(), err) | 
					
						
							|  |  |  | 	return token | 
					
						
							|  |  |  | } |