Compare commits

...

2 Commits

Author SHA1 Message Date
shopper
425c585e47 Support for smtp LOGIN authentication (#1048)
* finished to implement new mail client

* delete email_test.go
2020-09-04 15:45:29 +09:00
Kota Kanbe
4f1578b2d6 [WIP]fix(scan): collect a running version of kernel-devel (#1044)
* fix(scan): collect a running kernel-devel version

* refactor
2020-09-01 14:37:40 +09:00
8 changed files with 113 additions and 178 deletions

2
go.mod
View File

@@ -19,6 +19,8 @@ require (
github.com/boltdb/bolt v1.3.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.13.0
github.com/google/subcommands v1.2.0
github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-uuid v1.0.2

4
go.sum
View File

@@ -187,6 +187,10 @@ github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:/Zj4wYkg
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.13.0 h1:aC3Kc21TdfvXnuJXCQXuhnDXUldhc12qME/S7Y3Y94g=
github.com/emersion/go-smtp v0.13.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=

View File

@@ -100,7 +100,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
cveContents := vinfo.CveContents
if v, ok := vinfo.CveContents[ctype]; ok {
if v.LastModified.After(ovalContent.LastModified) {
util.Log.Debugf("%s, OvalID: %d ignroed: ",
util.Log.Debugf("%s, OvalID: %d ignored: ",
cve.CveID, defPacks.def.ID)
} else {
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)

View File

@@ -5,10 +5,11 @@ import (
"fmt"
"net"
"net/mail"
"net/smtp"
"strings"
"time"
sasl "github.com/emersion/go-sasl"
smtp "github.com/emersion/go-smtp"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
@@ -21,7 +22,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf
var message string
sender := NewEMailSender()
m := map[string]int{}
for _, r := range rs {
if conf.FormatOneEMail {
@@ -85,37 +85,50 @@ type EMailSender interface {
type emailSender struct {
conf config.SMTPConf
send func(string, smtp.Auth, string, []string, []byte) error
}
func smtps(emailConf config.SMTPConf, message string) (err error) {
auth := smtp.PlainAuth("",
emailConf.User,
emailConf.Password,
emailConf.SMTPAddr,
)
func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
var c *smtp.Client
var auth sasl.Client
emailConf := e.conf
//TLS Config
tlsConfig := &tls.Config{
ServerName: emailConf.SMTPAddr,
}
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
//New TLS connection
con, err := tls.Dial("tcp", smtpServer, tlsConfig)
if err != nil {
return xerrors.Errorf("Failed to create TLS connection: %w", err)
switch emailConf.SMTPPort {
case "465":
//New TLS connection
c, err = smtp.DialTLS(smtpServerAddr, tlsConfig)
if err != nil {
return xerrors.Errorf("Failed to create TLS connection to SMTP server: %w", err)
}
default:
c, err = smtp.Dial(smtpServerAddr)
if err != nil {
return xerrors.Errorf("Failed to create connection to SMTP server: %w", err)
}
}
defer con.Close()
defer c.Close()
c, err := smtp.NewClient(con, emailConf.SMTPAddr)
if err != nil {
return xerrors.Errorf("Failed to create new client: %w", err)
if err = c.Hello("localhost"); err != nil {
return xerrors.Errorf("Failed to send Hello command: %w", err)
}
if ok, _ := c.Extension("STARTTLS"); ok {
if err := c.StartTLS(tlsConfig); err != nil {
return xerrors.Errorf("Failed to STARTTLS: %w", err)
}
}
if ok, param := c.Extension("AUTH"); ok {
authList := strings.Split(param, " ")
auth = e.newSaslClient(authList)
}
if err = c.Auth(auth); err != nil {
return xerrors.Errorf("Failed to authenticate: %w", err)
}
if err = c.Mail(emailConf.From); err != nil {
if err = c.Mail(emailConf.From, nil); err != nil {
return xerrors.Errorf("Failed to send Mail command: %w", err)
}
for _, to := range emailConf.To {
@@ -169,38 +182,13 @@ func (e *emailSender) Send(subject, body string) (err error) {
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
if emailConf.User != "" && emailConf.Password != "" {
switch emailConf.SMTPPort {
case "465":
err := smtps(emailConf, message)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
default:
err = e.send(
smtpServer,
smtp.PlainAuth(
"",
emailConf.User,
emailConf.Password,
emailConf.SMTPAddr,
),
emailConf.From,
mailAddresses,
[]byte(message),
)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
err = e.sendMail(smtpServer, message)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
return nil
}
err = e.send(
smtpServer,
nil,
emailConf.From,
mailAddresses,
[]byte(message),
)
err = e.sendMail(smtpServer, message)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
@@ -209,5 +197,19 @@ func (e *emailSender) Send(subject, body string) (err error) {
// NewEMailSender creates emailSender
func NewEMailSender() EMailSender {
return &emailSender{config.Conf.EMail, smtp.SendMail}
return &emailSender{config.Conf.EMail}
}
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
for _, v := range authList {
switch v {
case "PLAIN":
auth := sasl.NewPlainClient("", e.conf.User, e.conf.Password)
return auth
case "LOGIN":
auth := sasl.NewLoginClient(e.conf.User, e.conf.Password)
return auth
}
}
return nil
}

View File

@@ -1,115 +0,0 @@
package report
import (
"net/smtp"
"reflect"
"strings"
"testing"
"github.com/future-architect/vuls/config"
)
type emailRecorder struct {
addr string
auth smtp.Auth
from string
to []string
body string
}
type mailTest struct {
in config.SMTPConf
out emailRecorder
}
var mailTests = []mailTest{
{
config.SMTPConf{
SMTPAddr: "127.0.0.1",
SMTPPort: "25",
From: "from@address.com",
To: []string{"to@address.com"},
Cc: []string{"cc@address.com"},
},
emailRecorder{
addr: "127.0.0.1:25",
auth: smtp.PlainAuth("", "", "", "127.0.0.1"),
from: "from@address.com",
to: []string{"to@address.com", "cc@address.com"},
body: "body",
},
},
{
config.SMTPConf{
SMTPAddr: "127.0.0.1",
SMTPPort: "25",
User: "vuls",
Password: "password",
From: "from@address.com",
To: []string{"to1@address.com", "to2@address.com"},
Cc: []string{"cc1@address.com", "cc2@address.com"},
},
emailRecorder{
addr: "127.0.0.1:25",
auth: smtp.PlainAuth(
"",
"vuls",
"password",
"127.0.0.1",
),
from: "from@address.com",
to: []string{"to1@address.com", "to2@address.com",
"cc1@address.com", "cc2@address.com"},
body: "body",
},
},
}
func TestSend(t *testing.T) {
for i, test := range mailTests {
f, r := mockSend(nil)
sender := &emailSender{conf: test.in, send: f}
subject := "subject"
body := "body"
if err := sender.Send(subject, body); err != nil {
t.Errorf("unexpected error: %s", err)
}
if r.addr != test.out.addr {
t.Errorf("#%d: wrong 'addr' field.\r\nexpected: %s\n got: %s", i, test.out.addr, r.addr)
}
if !reflect.DeepEqual(r.auth, test.out.auth) && r.auth != nil {
t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
}
if r.from != test.out.from {
t.Errorf("#%d: wrong 'from' field.\r\nexpected: %v\n got: %v", i, test.out.from, r.from)
}
if !reflect.DeepEqual(r.to, test.out.to) {
t.Errorf("#%d: wrong 'to' field.\r\nexpected: %v\n got: %v", i, test.out.to, r.to)
}
if r.body != test.out.body {
t.Errorf("#%d: wrong 'body' field.\r\nexpected: %v\n got: %v", i, test.out.body, r.body)
}
}
}
func mockSend(errToReturn error) (func(string, smtp.Auth, string, []string, []byte) error, *emailRecorder) {
r := new(emailRecorder)
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
// Split into header and body
messages := strings.Split(string(msg), "\r\n\r\n")
body := messages[1]
*r = emailRecorder{addr, a, from, to, body}
return errToReturn
}, r
}

View File

@@ -279,13 +279,13 @@ func (o *redhatBase) parseInstalledPackages(stdout string) (models.Packages, mod
// openssl 0 1.0.1e 30.el6.11 x86_64
lines := strings.Split(stdout, "\n")
for _, line := range lines {
if trimed := strings.TrimSpace(line); len(trimed) != 0 {
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
pack, err := o.parseInstalledPackagesLine(line)
if err != nil {
return nil, nil, err
}
// Kernel package may be isntalled multiple versions.
// `Kernel` and `kernel-devel` package may be installed multiple versions.
// From the viewpoint of vulnerability detection,
// pay attention only to the running kernel
isKernel, running := isRunningKernel(pack, o.Distro.Family, o.Kernel)

View File

@@ -25,10 +25,10 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
}{
{
in: `openssl 0 1.0.1e 30.el6.11 x86_64
Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64
kernel 0 2.6.32 696.20.1.el6 x86_64
kernel 0 2.6.32 696.20.3.el6 x86_64
kernel 0 2.6.32 695.20.3.el6 x86_64`,
Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64
kernel 0 2.6.32 696.20.1.el6 x86_64
kernel 0 2.6.32 696.20.3.el6 x86_64
kernel 0 2.6.32 695.20.3.el6 x86_64`,
kernel: models.Kernel{},
packages: models.Packages{
"openssl": models.Package{
@@ -50,10 +50,46 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
},
{
in: `openssl 0 1.0.1e 30.el6.11 x86_64
Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64
kernel 0 2.6.32 696.20.1.el6 x86_64
kernel 0 2.6.32 696.20.3.el6 x86_64
kernel 0 2.6.32 695.20.3.el6 x86_64`,
Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64
kernel 0 2.6.32 696.20.1.el6 x86_64
kernel 0 2.6.32 696.20.3.el6 x86_64
kernel 0 2.6.32 695.20.3.el6 x86_64
kernel-devel 0 2.6.32 696.20.1.el6 x86_64
kernel-devel 0 2.6.32 696.20.3.el6 x86_64
kernel-devel 0 2.6.32 695.20.3.el6 x86_64`,
kernel: models.Kernel{Release: "2.6.32-696.20.3.el6.x86_64"},
packages: models.Packages{
"openssl": models.Package{
Name: "openssl",
Version: "1.0.1e",
Release: "30.el6.11",
},
"Percona-Server-shared-56": models.Package{
Name: "Percona-Server-shared-56",
Version: "1:5.6.19",
Release: "rel67.0.el6",
},
"kernel": models.Package{
Name: "kernel",
Version: "2.6.32",
Release: "696.20.3.el6",
},
"kernel-devel": models.Package{
Name: "kernel-devel",
Version: "2.6.32",
Release: "696.20.3.el6",
},
},
},
{
in: `openssl 0 1.0.1e 30.el6.11 x86_64
Percona-Server-shared-56 1 5.6.19 rel67.0.el6 x84_64
kernel 0 2.6.32 696.20.1.el6 x86_64
kernel 0 2.6.32 696.20.3.el6 x86_64
kernel 0 2.6.32 695.20.3.el6 x86_64
kernel-devel 0 2.6.32 696.20.1.el6 x86_64
kernel-devel 0 2.6.32 696.20.3.el6 x86_64
kernel-devel 0 2.6.32 695.20.3.el6 x86_64`,
kernel: models.Kernel{Release: "2.6.32-695.20.3.el6.x86_64"},
packages: models.Packages{
"openssl": models.Package{
@@ -71,6 +107,11 @@ func TestParseInstalledPackagesLinesRedhat(t *testing.T) {
Version: "2.6.32",
Release: "695.20.3.el6",
},
"kernel-devel": models.Package{
Name: "kernel-devel",
Version: "2.6.32",
Release: "695.20.3.el6",
},
},
},
}

View File

@@ -22,7 +22,8 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
return false, false
case config.RedHat, config.Oracle, config.CentOS, config.Amazon:
if pack.Name == "kernel" {
switch pack.Name {
case "kernel", "kernel-devel":
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
return true, kernel.Release == ver
}