| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | package security | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gin-gonic/gin" | 
					
						
							|  |  |  | 	"github.com/go-fed/httpsig" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignatureCheck checks whether an incoming http request has been signed. If so, it will check if the domain | 
					
						
							|  |  |  | // that signed the request is permitted to access the server. If it is permitted, the handler will set the key | 
					
						
							|  |  |  | // verifier in the gin context for use down the line. | 
					
						
							|  |  |  | func (m *Module) SignatureCheck(c *gin.Context) { | 
					
						
							|  |  |  | 	l := m.log.WithField("func", "DomainBlockChecker") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// set this extra field for signature validation | 
					
						
							|  |  |  | 	c.Request.Header.Set("host", m.config.Host) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create the verifier from the request | 
					
						
							|  |  |  | 	// if the request is signed, it will have a signature header | 
					
						
							|  |  |  | 	verifier, err := httpsig.NewVerifier(c.Request) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		// the request was signed! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// The key ID should be given in the signature so that we know where to fetch it from the remote server. | 
					
						
							|  |  |  | 		// This will be something like https://example.org/users/whatever_requesting_user#main-key | 
					
						
							|  |  |  | 		requestingPublicKeyID, err := url.Parse(verifier.KeyId()) | 
					
						
							|  |  |  | 		if err == nil && requestingPublicKeyID != nil { | 
					
						
							|  |  |  | 			// we managed to parse the url! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// if the domain is blocked we want to bail as early as possible | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 			blocked, err := m.db.IsURIBlocked(requestingPublicKeyID) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				l.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err) | 
					
						
							|  |  |  | 				c.AbortWithStatus(http.StatusInternalServerError) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 			if blocked { | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 				l.Infof("domain %s is blocked", requestingPublicKeyID.Host) | 
					
						
							|  |  |  | 				c.AbortWithStatus(http.StatusForbidden) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// set the verifier on the context here to save some work further down the line | 
					
						
							|  |  |  | 			c.Set(string(util.APRequestingPublicKeyVerifier), verifier) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |