-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathutil.go
More file actions
141 lines (122 loc) · 4.2 KB
/
Copy pathutil.go
File metadata and controls
141 lines (122 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"
"net"
"net/http"
"strings"
p "github.com/prometheus/client_golang/prometheus"
)
/*
generateRequestID returns a random 16-character URL-safe base64 string for
request tracing. It uses 12 bytes of crypto/rand which aligns exactly to
16 base64 characters with no padding.
*/
func generateRequestID() string {
b := make([]byte, 12)
rand.Read(b)
return base64.URLEncoding.EncodeToString(b)
}
/*
getIPName returns the client name of a given IP Address ip. If it is not found,
an error is returned.
*/
func getIPName(ip string) (string, error) {
naddr := net.ParseIP(ip)
if naddr == nil {
return "", fmt.Errorf("invalid IP Address given: %s", ip)
}
for _, c := range clients {
for _, ip := range append(c.IPv6(), c.IPv4()...) {
if ip.Equal(naddr) {
return c.Name(), nil
}
}
}
return "", fmt.Errorf("IP Address not a client")
}
/*
getPassName returns the client name for a given Password password. If it is not
found, an error is returned.
*/
func getPassName(password string) (string, error) {
if password == "" {
return "", fmt.Errorf("password was empty. did not match")
}
passHash := sha256.Sum256([]byte(password))
for _, c := range clients {
clientHash := sha256.Sum256([]byte(c.Password))
if subtle.ConstantTimeCompare(passHash[:], clientHash[:]) == 1 {
return c.Name(), nil
}
}
return "", fmt.Errorf("password did not match client database")
}
/*
requestBasicAuth is an HTTP Handler wrapper that will require the passed
handler to be served only if the HTTP Basic Authentication Credentials are
correct.
*/
func requestBasicAuth(username, password, realm string, pa p.CounterVec, handler http.HandlerFunc) http.HandlerFunc {
/* Calculate the SHA-256 Hash of the Required Username and Password */
RequiredUserNameHash := sha256.Sum256([]byte(username))
RequiredPasswordHash := sha256.Sum256([]byte(password))
return func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
/* Calculate the SHA-256 Hash of the Given Username and Password */
PassedUsername := sha256.Sum256([]byte(user))
PassedPassword := sha256.Sum256([]byte(pass))
/*
subtle.ConstantTimeCompare is used so the username and password
comparison take constant time, and therefore do not leak
information about the length of the password, or allow time-based
side channel attacks. However, in order to prevent password length
guessing, SHA-256 is used, which is always a constant size.
Calculation of the SHA-256 hash can still be attacked, but isn't as
likely, since the inputs are constants.
*/
if !ok {
pa.With(p.Labels{"success": "false", "error": "HTTP-Basic-Auth-Not-Ok"}).Inc()
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)
w.Write([]byte("You need to supply the correct credentials for this page.\n"))
return
}
if subtle.ConstantTimeCompare(PassedUsername[:], RequiredUserNameHash[:]) != 1 {
pa.With(p.Labels{"success": "false", "error": "Incorrect-Username"}).Inc()
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)
w.Write([]byte("You need to supply the correct credentials for this page.\n"))
return
}
if subtle.ConstantTimeCompare(PassedPassword[:], RequiredPasswordHash[:]) != 1 {
pa.With(p.Labels{"success": "false", "error": "Incorrect-Password"}).Inc()
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)
w.Write([]byte("You need to supply the correct credentials for this page.\n"))
return
}
pa.With(p.Labels{"success": "true", "error": ""}).Inc()
handler(w, r)
}
}
/*
isValidFilename checks if a user-supplied filename is safe for use as an object
storage key. It rejects null bytes, path traversal attempts (`.` and `..`), and
empty path components (leading/trailing/double slashes). Technically `.` and
`..` don't cause problems in object storage but it doesn't hurt to drop them.
*/
func isValidFilename(name string) bool {
if strings.Contains(name, "\x00") {
return false
}
for _, component := range strings.Split(name, "/") {
if component == "." || component == ".." || component == "" {
return false
}
}
return true
}