Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow hmac authentication for read-file #449

Merged
merged 1 commit into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion wavesrv/cmd/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
"github.com/wavetermdev/waveterm/wavesrv/pkg/cmdrunner"
"github.com/wavetermdev/waveterm/wavesrv/pkg/pcloud"
"github.com/wavetermdev/waveterm/wavesrv/pkg/promptenc"
"github.com/wavetermdev/waveterm/wavesrv/pkg/releasechecker"
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote"
"github.com/wavetermdev/waveterm/wavesrv/pkg/rtnstate"
Expand Down Expand Up @@ -695,6 +696,35 @@ func AuthKeyMiddleWare(next http.Handler) http.Handler {
})
}

func AuthKeyWrapAllowHmac(fn WebFnType) WebFnType {
return func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
if reqAuthKey == "" {
// try hmac
qvals := r.URL.Query()
if !qvals.Has("hmac") {
w.WriteHeader(500)
w.Write([]byte("no x-authkey header"))
return
}
hmacOk, err := promptenc.ValidateUrlHmac([]byte(GlobalAuthKey), r.URL.Path, qvals)
if err != nil || !hmacOk {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("error validating hmac")))
return
}
// fallthrough (hmac is valid)
} else if reqAuthKey != GlobalAuthKey {
w.WriteHeader(500)
w.Write([]byte("x-authkey header is invalid"))
return
}
w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache)
fn(w, r)
}

}

func AuthKeyWrap(fn WebFnType) WebFnType {
return func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
Expand Down Expand Up @@ -921,7 +951,7 @@ func main() {
gr.HandleFunc("/api/get-client-data", AuthKeyWrap(HandleGetClientData))
gr.HandleFunc("/api/set-winsize", AuthKeyWrap(HandleSetWinSize))
gr.HandleFunc("/api/log-active-state", AuthKeyWrap(HandleLogActiveState))
gr.HandleFunc("/api/read-file", AuthKeyWrap(HandleReadFile))
gr.HandleFunc("/api/read-file", AuthKeyWrapAllowHmac(HandleReadFile))
gr.HandleFunc("/api/write-file", AuthKeyWrap(HandleWriteFile)).Methods("POST")
configPath := path.Join(scbase.GetWaveHomeDir(), "config") + "/"
log.Printf("[wave] config path: %q\n", configPath)
Expand Down
52 changes: 52 additions & 0 deletions wavesrv/pkg/promptenc/hmac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

package promptenc

import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"net/url"
)

func ComputeUrlHmac(key []byte, baseUrl string, qvals url.Values) (string, error) {
if qvals.Has("nonce") {
return "", fmt.Errorf("nonce is required for hmac")
}
if qvals.Has("hmac") {
return "", fmt.Errorf("hmac is already present")
}
encStr := baseUrl + "?" + qvals.Encode()
mac := hmac.New(sha256.New, key)
mac.Write([]byte(encStr))
rtn := mac.Sum(nil)
return base64.URLEncoding.EncodeToString(rtn), nil
}

func copyUrlValues(src url.Values) url.Values {
rtn := make(url.Values)
for k, v := range src {
rtn[k] = v
}
return rtn
}

func ValidateUrlHmac(key []byte, baseUrl string, qvalsOrig url.Values) (bool, error) {
qvals := copyUrlValues(qvalsOrig)
hmacStr := qvals.Get("hmac")
if hmacStr == "" {
return false, fmt.Errorf("no hmac key found"))
}
qvals.Del("hmac")
encStr := baseUrl + "?" + qvals.Encode()
mac := hmac.New(sha256.New, key)
mac.Write([]byte(encStr))
expected := mac.Sum(nil)
actual, err := base64.URLEncoding.DecodeString(hmacStr)
if err != nil {
return false, fmt.Errorf("error decoding hmac: %w", err)
}
return hmac.Equal(expected, actual), nil
}
Loading