security: Remote crash in OAuth2 GHSA-45m3-398w-m2m9 Thanks @kule500 for the responsible disclosure. CVE to follow.

This commit is contained in:
jamesread 2026-03-01 23:52:25 +00:00
parent aa2bd95ccb
commit f044d90d55
1 changed files with 27 additions and 17 deletions

View File

@ -11,6 +11,7 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"sync"
"time" "time"
authTypes "github.com/OliveTin/OliveTin/internal/auth/authpublic" authTypes "github.com/OliveTin/OliveTin/internal/auth/authpublic"
@ -21,6 +22,7 @@ import (
type OAuth2Handler struct { type OAuth2Handler struct {
cfg *config.Config cfg *config.Config
mu sync.RWMutex
registeredStates map[string]*oauth2State registeredStates map[string]*oauth2State
registeredProviders map[string]*oauth2.Config registeredProviders map[string]*oauth2.Config
} }
@ -144,11 +146,13 @@ func (h *OAuth2Handler) HandleOAuthLogin(w http.ResponseWriter, r *http.Request)
return return
} }
h.mu.Lock()
h.registeredStates[state] = &oauth2State{ h.registeredStates[state] = &oauth2State{
providerConfig: provider, providerConfig: provider,
providerName: providerName, providerName: providerName,
Username: "", Username: "",
} }
h.mu.Unlock()
h.setOAuthCallbackCookie(w, r, "olivetin-sid-oauth", state) h.setOAuthCallbackCookie(w, r, "olivetin-sid-oauth", state)
@ -177,7 +181,9 @@ func (h *OAuth2Handler) checkOAuthCallbackCookie(w http.ResponseWriter, r *http.
return nil, state, false return nil, state, false
} }
h.mu.RLock()
registeredState, ok := h.registeredStates[state] registeredState, ok := h.registeredStates[state]
h.mu.RUnlock()
if !ok { if !ok {
log.Errorf("State not found in server: %v", state) log.Errorf("State not found in server: %v", state)
http.Error(w, "State not found in server", http.StatusBadRequest) http.Error(w, "State not found in server", http.StatusBadRequest)
@ -287,8 +293,10 @@ func (h *OAuth2Handler) HandleOAuthCallback(w http.ResponseWriter, r *http.Reque
userInfoClient := h.createUserInfoClient(ctx, registeredState.providerConfig, tok, clientSettings) userInfoClient := h.createUserInfoClient(ctx, registeredState.providerConfig, tok, clientSettings)
userinfo := getUserInfo(h.cfg, userInfoClient, providerConfig) userinfo := getUserInfo(h.cfg, userInfoClient, providerConfig)
h.mu.Lock()
h.registeredStates[state].Username = userinfo.Username h.registeredStates[state].Username = userinfo.Username
h.registeredStates[state].Usergroup = h.computeUsergroup(userinfo, providerConfig) h.registeredStates[state].Usergroup = h.computeUsergroup(userinfo, providerConfig)
h.mu.Unlock()
http.Redirect(w, r, "/", http.StatusFound) http.Redirect(w, r, "/", http.StatusFound)
} }
@ -366,34 +374,36 @@ func getDataField(data map[string]any, field string) string {
return stringVal return stringVal
} }
func (h *OAuth2Handler) lookupOAuth2UserByState(state string) (*authTypes.AuthenticatedUser, bool) {
h.mu.RLock()
serverState, found := h.registeredStates[state]
if !found {
h.mu.RUnlock()
return nil, false
}
user := &authTypes.AuthenticatedUser{
Username: serverState.Username,
UsergroupLine: serverState.Usergroup,
Provider: "oauth2",
SID: state,
}
h.mu.RUnlock()
return user, true
}
func (h *OAuth2Handler) CheckUserFromOAuth2Cookie(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser { func (h *OAuth2Handler) CheckUserFromOAuth2Cookie(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser {
cookie, err := context.Request.Cookie("olivetin-sid-oauth") cookie, err := context.Request.Cookie("olivetin-sid-oauth")
if err != nil || cookie.Value == "" {
user := &authTypes.AuthenticatedUser{}
if err != nil {
return nil return nil
} }
if cookie.Value == "" { user, found := h.lookupOAuth2UserByState(cookie.Value)
return nil
}
serverState, found := h.registeredStates[cookie.Value]
if !found { if !found {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"sid": cookie.Value, "sid": cookie.Value,
"provider": "oauth2", "provider": "oauth2",
}).Warnf("Stale session") }).Warnf("Stale session")
return nil return nil
} }
user.Username = serverState.Username
user.UsergroupLine = serverState.Usergroup
user.Provider = "oauth2"
user.SID = cookie.Value
return user return user
} }