admin: new route to generate htpasswd corresponding to certificate in use by team
This commit is contained in:
parent
6925614f49
commit
2623d9dd61
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/base32"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"srs.epita.fr/fic-server/admin/pki"
|
"srs.epita.fr/fic-server/admin/pki"
|
||||||
"srs.epita.fr/fic-server/libfic"
|
"srs.epita.fr/fic-server/libfic"
|
||||||
@ -22,6 +24,10 @@ import (
|
|||||||
var TeamsDir string
|
var TeamsDir string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
router.GET("/api/htpasswd", apiHandler(
|
||||||
|
func(httprouter.Params, []byte) (interface{}, error) {
|
||||||
|
return genHtpasswd()
|
||||||
|
}))
|
||||||
router.GET("/api/ca/", apiHandler(infoCA))
|
router.GET("/api/ca/", apiHandler(infoCA))
|
||||||
router.GET("/api/ca.pem", apiHandler(getCAPEM))
|
router.GET("/api/ca.pem", apiHandler(getCAPEM))
|
||||||
router.POST("/api/ca/new", apiHandler(
|
router.POST("/api/ca/new", apiHandler(
|
||||||
@ -57,6 +63,49 @@ func init() {
|
|||||||
func(cert fic.Certificate, _ []byte) (interface{}, error) { return cert.Revoke() })))
|
func(cert fic.Certificate, _ []byte) (interface{}, error) { return cert.Revoke() })))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func genHtpasswd() (ret string, err error) {
|
||||||
|
var teams []fic.Team
|
||||||
|
teams, err = fic.GetTeams()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
var serials []uint64
|
||||||
|
serials, err = pki.GetTeamSerials(TeamsDir, team.Id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(serials) == 0 {
|
||||||
|
// Don't include teams that don't have associated certificates
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serial := range serials {
|
||||||
|
var cert fic.Certificate
|
||||||
|
cert, err = fic.GetCertificate(serial)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert.Revoked != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 5)
|
||||||
|
if _, err = rand.Read(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
salt := base32.StdEncoding.EncodeToString(b)
|
||||||
|
|
||||||
|
ret += fmt.Sprintf("%s:$apr1$%s$%s\n", strings.ToLower(team.Name), salt, fic.Apr1Md5(cert.Password, salt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func infoCA(_ httprouter.Params, _ []byte) (interface{}, error) {
|
func infoCA(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||||
_, cacert, err := pki.LoadCA()
|
_, cacert, err := pki.LoadCA()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package fic
|
package fic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -10,3 +12,81 @@ func ToURLid(str string) string {
|
|||||||
re := regexp.MustCompile("[^a-zA-Z0-9]+")
|
re := regexp.MustCompile("[^a-zA-Z0-9]+")
|
||||||
return strings.TrimSuffix(re.ReplaceAllLiteralString(str, "-"), "-")
|
return strings.TrimSuffix(re.ReplaceAllLiteralString(str, "-"), "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apr1Md5 computes a usable hash for basic auth in Apache and nginx.
|
||||||
|
// Function copied from https://github.com/jimstudt/http-authentication/blob/master/basic/md5.go
|
||||||
|
// as it was not exported in package and comes with other unwanted functions.
|
||||||
|
// Original source code under MIT.
|
||||||
|
func Apr1Md5(password string, salt string) string {
|
||||||
|
initBin := md5.Sum([]byte(password + salt + password))
|
||||||
|
|
||||||
|
initText := bytes.NewBufferString(password + "$apr1$" + salt)
|
||||||
|
|
||||||
|
for i := len(password); i > 0; i -= 16 {
|
||||||
|
lim := i
|
||||||
|
if lim > 16 {
|
||||||
|
lim = 16
|
||||||
|
}
|
||||||
|
initText.Write(initBin[0:lim])
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(password); i > 0; i >>= 1 {
|
||||||
|
if (i & 1) == 1 {
|
||||||
|
initText.WriteByte(byte(0))
|
||||||
|
} else {
|
||||||
|
initText.WriteByte(password[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bin := md5.Sum(initText.Bytes())
|
||||||
|
|
||||||
|
n := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
n.Reset()
|
||||||
|
|
||||||
|
if (i & 1) == 1 {
|
||||||
|
n.WriteString(password)
|
||||||
|
} else {
|
||||||
|
n.Write(bin[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if i%3 != 0 {
|
||||||
|
n.WriteString(salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i%7 != 0 {
|
||||||
|
n.WriteString(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i & 1) == 1 {
|
||||||
|
n.Write(bin[:])
|
||||||
|
} else {
|
||||||
|
n.WriteString(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
bin = md5.Sum(n.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
fill := func(a byte, b byte, c byte) {
|
||||||
|
v := (uint(a) << 16) + (uint(b) << 8) + uint(c)
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
result.WriteByte("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[v&0x3f])
|
||||||
|
v >>= 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fill(bin[0], bin[6], bin[12])
|
||||||
|
fill(bin[1], bin[7], bin[13])
|
||||||
|
fill(bin[2], bin[8], bin[14])
|
||||||
|
fill(bin[3], bin[9], bin[15])
|
||||||
|
fill(bin[4], bin[10], bin[5])
|
||||||
|
fill(0, 0, bin[11])
|
||||||
|
|
||||||
|
resultString := string(result.Bytes()[0:22])
|
||||||
|
|
||||||
|
return resultString
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user