yeah so as a temporary measure, i'm using ssh-keygen for now.

but i'll need to natively incorporate it still.
This commit is contained in:
brent s. 2021-07-03 23:01:58 -04:00
parent d7ffbea913
commit 7f98e7aa15
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
9 changed files with 323 additions and 19 deletions

View File

@ -31,7 +31,7 @@ import (
"r00t2.io/sysutils/paths"
)

func TestConfig(config *[]byte) (bool, error) {
func ChkConfig(config *[]byte) (bool, error) {
var sysPaths []string
var binPath string
var tmpConf *os.File

View File

@ -18,6 +18,17 @@

package dh

import (
"io/ioutil"
"os"
"os/exec"
"strings"

"github.com/Masterminds/semver"
"r00t2.io/sshsecure/moduli"
"r00t2.io/sshsecure/utils"
)

/*
OpenSSH does prime generation and primality checking a *little* weird.

@ -35,4 +46,129 @@ package dh
And that's why I'm a sad panda and porting moduli.c to native Golang.
*/

func SieveLarge()
// Generate builds a slice of moduli.Entry entries.
// TODO: DO THIS NATIVELY. Idealy with goroutines and a channel? buffer? for the primes and sieving.
func Generate() (moduliEntries []moduli.Entry, err error) {

var outFile *os.File
var filteredFile string
var raw []byte
var m *moduli.Moduli = new(moduli.Moduli)

if outFile, err = genPrimes(); err != nil {
return
}

if filteredFile, err = sieve(outFile); err != nil {
return
}

if raw, err = ioutil.ReadFile(filteredFile); err != nil {
return
}

if err = moduli.Unmarshal(raw, m); err != nil {
return
}

moduliEntries = m.Groups

return
}

// genPrimes builds a slice of acceptable primes for sieve.
// TODO: DO THIS NATIVELY.
func genPrimes() (outFile *os.File, err error) {

var cmdStr []string = []string{"ssh-keygen", "-q"}
var cmdStr2 []string
var cmd *exec.Cmd
var sshVer utils.SshVerInfo
// These are various versions we need to compare against for determining features.
var ver81 *semver.Version

if outFile, err = ioutil.TempFile("", ".SSHSecure.moduli.*"); err != nil {
return
}
if err = outFile.Close(); err != nil {
return
}
if err = os.Remove(outFile.Name()); err != nil {
return
}

if sshVer, err = utils.GetSshVer(""); err != nil {
return
}

// They changed the command syntax/options in 8.1. We need to determine if we're at or above that version or not.
if ver81, err = semver.NewVersion("8.1.0"); err != nil {
return
}

if sshVer.SemVer.Compare(ver81) >= 0 {
cmdStr2 = []string{
"-M", "generate",
"-O", "bits=4096",
outFile.Name(),
}
} else {
cmdStr2 = []string{
"-b", "4096",
"-G", outFile.Name(),
}
}
cmdStr = append(cmdStr, cmdStr2...)

cmd = exec.Command(cmdStr[0], strings.Join(cmdStr[1:], " "))
if err = cmd.Run(); err != nil {
return
}

return
}

// sieve filters out unsuitable primes. See #1 and #2 in the comments at the top of this file.
// TODO: DO THIS NATIVELY.
func sieve(inFile *os.File) (newFile string, err error) {

var cmdStr []string = []string{"ssh-keygen", "-q"}
var cmdStr2 []string
var cmd *exec.Cmd
var sshVer utils.SshVerInfo
// These are various versions we need to compare against for determining features.
var ver81 *semver.Version

if sshVer, err = utils.GetSshVer(""); err != nil {
return
}

newFile = inFile.Name() + ".filtered"

// They changed the command syntax/options in 8.1. We need to determine if we're at or above that version or not.
if ver81, err = semver.NewVersion("8.1.0"); err != nil {
return
}

if sshVer.SemVer.Compare(ver81) >= 0 {
cmdStr2 = []string{
"-M", "screen",
"-O", "bits=4096",
"-f", inFile.Name(),
newFile,
}
} else {
cmdStr2 = []string{
"-T", newFile,
"-f", inFile.Name(),
}
}
cmdStr = append(cmdStr, cmdStr2...)

cmd = exec.Command(cmdStr[0], strings.Join(cmdStr[1:], " "))
if err = cmd.Run(); err != nil {
return
}

return
}

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.16

require (
github.com/Luzifer/go-dhparam v1.1.0
github.com/Masterminds/semver v1.5.0
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
github.com/go-restruct/restruct v1.2.0-alpha
github.com/pkg/errors v0.9.1

2
go.sum
View File

@ -1,5 +1,7 @@
github.com/Luzifer/go-dhparam v1.1.0 h1:uJXDwqAVy1H4zWjmsYVmaa9yUD2Pm3SsdW4KU8d27zc=
github.com/Luzifer/go-dhparam v1.1.0/go.mod h1:3Kuj59C67/G2EzQHjUzAryaAa70K5fqvStR2VkFLszU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=

View File

@ -24,14 +24,14 @@ import (
"errors"
"fmt"
"net/http"
`time`
"time"

`github.com/Luzifer/go-dhparam`
"github.com/Luzifer/go-dhparam"
"golang.org/x/crypto/sha3"
)

// NewModuli returns a Moduli populated with Entry items.
func NewModuli(usePreGen ... bool) (m *Moduli, err error) {
func NewModuli(usePreGen ...bool) (m *Moduli, err error) {

var doPreGen bool

@ -117,13 +117,13 @@ func Generate(m *Moduli) (err error) {
var e Entry

e = Entry{
Time: time.Now(),
Size: bitLen,
Time: time.Now(),
Size: bitLen,
Generator: uint8(generator),
/*
Type: 0,
Tests: 0,
Trials: 0,
Type: 0,
Tests: 0,
Trials: 0,
*/
}

@ -143,7 +143,7 @@ func Generate(m *Moduli) (err error) {
e.Modulus = *dh.P

// TODO: https://stackoverflow.com/questions/18499352/golang-concurrency-how-to-append-to-the-same-slice-from-different-goroutines
m.Params = append(m.Params, e)
m.Groups = append(m.Groups, e)
}

}

View File

@ -34,13 +34,12 @@ var reSkipLine, _ = regexp.Compile(`^\s*(#.*)?$`)

// Marshal returns the /etc/ssh/moduli format of m.
// Format of: Time Type Tests Tries Size Generator Modulus
// TODO: remember to write newline at end
func (m *Moduli) Marshal() (bytesOut []byte, err error) {

var b bytes.Buffer

b.Write([]byte(header))
for _, i := range m.Params {
for _, i := range m.Groups {
line, err := i.marshalEntry()
if err != nil {
return nil, err
@ -49,6 +48,8 @@ func (m *Moduli) Marshal() (bytesOut []byte, err error) {
}
}

b.Write([]byte("\n"))

bytesOut = b.Bytes()

return
@ -98,7 +99,7 @@ func Unmarshal(data []byte, m *Moduli) (err error) {
entries = append(entries, e)
}

m.Params = entries
m.Groups = entries

return
}
@ -149,7 +150,7 @@ func (m *Moduli) Harden() (err error) {

var entries []Entry

for _, e := range m.Params {
for _, e := range m.Groups {

e.Time = time.Now()

@ -157,9 +158,9 @@ func (m *Moduli) Harden() (err error) {
entries = append(entries, e)
}
}
m.Params = entries
m.Groups = entries

if len(m.Params) < recMinMod {
if len(m.Groups) < recMinMod {
err = errors.New("does not meet recommended minimum moduli")
return
}

View File

@ -23,10 +23,10 @@ import (
"time"
)

// Moduli contains all data needed for generated /etc/ssh/moduli. of ModuliEntry entries.
// Moduli contains all data needed for generated /etc/ssh/moduli of Entry entries.
type Moduli struct {
Header string
Params []Entry
Groups []Entry
}

// Entry is a struct reflecting the format of a single /etc/ssh/moduli entry. See moduli(5) for details.

143
utils/func.go Normal file
View File

@ -0,0 +1,143 @@
package utils

import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"

"github.com/Masterminds/semver"
)

// GetSshVer returns an SshVerInfo. If verStr is an empty string, get the version automatically.
func GetSshVer(verStr string) (ver SshVerInfo, err error) {

var newVerStr string
var sslVer OpenSslInfo
var sslVerStr string
var ptrn *regexp.Regexp
var matches []string
var verSlice [3]uint
var n int
var sver *semver.Version

if verStr == "" {
var out []byte
cmd := exec.Command("ssh", "-V")
if err = cmd.Run(); err != nil {
return
}
if out, err = cmd.Output(); err != nil {
return
}
verStr = string(out)
}

if ptrn, err = regexp.Compile(`^(?:Open|Sun_)SSH_(?P<sshver>[0-9.]+)(?:p?(?P<patchver>[0-9.]+))?,\s*(?P<sslverstr>.*)?$`); err != nil {
return
}

matches = ptrn.FindStringSubmatch(verStr)

// Reformat verStr to the combined version string.
newVerStr = fmt.Sprintf(
"%v.%v",
matches[ptrn.SubexpIndex("sshver")],
matches[ptrn.SubexpIndex("patchver")],
)

sslVerStr = matches[ptrn.SubexpIndex("sslverstr")]

if sslVer, err = GetSslVer(sslVerStr); err != nil {
return
}

for idx, s := range strings.Split(newVerStr, ".") {
if n, err = strconv.Atoi(s); err != nil {
return
}
verSlice[idx] = uint(n)
}

if sver, err = semver.NewVersion(newVerStr); err != nil {
return
}

ver = SshVerInfo{
Major: verSlice[0],
Minor: verSlice[1],
Patch: verSlice[2],
Raw: verStr,
SslInfo: sslVer,
SemVer: *sver,
}

return
}

// GetSslVer returns an OpenSslInfo. If verStr is an empty string, get the version automatically.
func GetSslVer(verStr string) (ver OpenSslInfo, err error) {

var matches []string
var verSlice [3]uint
var newVerStr string
var ptrn *regexp.Regexp
var sver *semver.Version
// TODO: confirm the day is zero-padded.
var tfmt string = `02 Jan 2006`
var n int
var t time.Time

if verStr == "" {
var out []byte
cmd := exec.Command("openssl", "version")
if err = cmd.Run(); err != nil {
return
}
if out, err = cmd.Output(); err != nil {
return
}
verStr = string(out)
}

if ptrn, err = regexp.Compile(`^(?:Open|Sun_)SSL\s+(FIPS\s+)?(?P<sslver>[0-9.]+)(?P<sslrel>[A-Za-z]+)?\s+(?P<time>(?P<day>[0-9]{1,2})\s(?P<month>[A-Za-z]+)\s(?P<year>[0-9]{4}))$`); err != nil {
return
}

matches = ptrn.FindStringSubmatch(verStr)

for idx, s := range strings.Split(matches[ptrn.SubexpIndex("sslver")], ".") {
if n, err = strconv.Atoi(s); err != nil {
return
}
verSlice[idx] = uint(n)
}

if t, err = time.Parse(tfmt, matches[ptrn.SubexpIndex("time")]); err != nil {
return
}

newVerStr = fmt.Sprintf(
"%v-%v",
matches[ptrn.SubexpIndex("sslver")],
matches[ptrn.SubexpIndex("sslrel")],
)

if sver, err = semver.NewVersion(newVerStr); err != nil {
return
}

ver = OpenSslInfo{
Major: verSlice[0],
Minor: verSlice[1],
Patch: verSlice[2],
Revision: matches[ptrn.SubexpIndex("sslrel")],
BuildDate: t,
Raw: verStr,
SemVer: *sver,
}

return
}

View File

@ -2,9 +2,30 @@ package utils

import (
"time"

"github.com/Masterminds/semver"
)

type runInfo struct {
TimeStarted time.Time
Pid int
}

type SshVerInfo struct {
Major uint
Minor uint
Patch uint
Raw string
SemVer semver.Version
SslInfo OpenSslInfo
}

type OpenSslInfo struct {
Major uint
Minor uint
Patch uint
Revision string
BuildDate time.Time
Raw string
SemVer semver.Version
}