commit d06d136601d6366cbf42434caf61ebc7b9a3b98e Author: Gurkengewuerz Date: Mon Nov 4 21:04:16 2019 +0100 init repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..215ad5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +config.ini diff --git a/config.sample.ini b/config.sample.ini new file mode 100644 index 0000000..68aa520 --- /dev/null +++ b/config.sample.ini @@ -0,0 +1,11 @@ +version = 0.1 +service_name = netire-cryptall + +[ldap] +host = ldap.exmaple.com +port = 636 +bind_dn = +bind_pw = +search_base = ou=people,dc=user,dc=mc8051,dc=de +query_filter = (&(mail=%s)(pgpEnabled=True)(objectClass=person)) +key_attribute = pgpKey \ No newline at end of file diff --git a/gpgit.go b/gpgit.go new file mode 100644 index 0000000..21eb295 --- /dev/null +++ b/gpgit.go @@ -0,0 +1,167 @@ +package main + +import ( + "bytes" + "crypto/tls" + "fmt" + "golang.org/x/crypto/openpgp" + "io" + "io/ioutil" + "log" + "os" + "regexp" + "strings" + + "github.com/emersion/go-message" + "github.com/emersion/go-pgpmime" + "gopkg.in/ini.v1" + "gopkg.in/ldap.v3" +) + +var config *ini.File + +func getArmoredKeyRing(recipient *string) (string, error) { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + + l, err := ldap.DialTLS( + "tcp", + fmt.Sprintf("%s:%s", config.Section("ldap").Key("host").String(), config.Section("ldap").Key("port").String()), + tlsConfig) + + if err != nil { + log.Fatal(err) + } + defer l.Close() + + keyAttribute := config.Section("ldap").Key("key_attribute").String() + searchRequest := ldap.NewSearchRequest( + config.Section("ldap").Key("search_base").String(), + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf(config.Section("ldap").Key("query_filter").String(), *recipient), + []string{"dn", "uid", keyAttribute}, // A list attributes to retrieve + nil, + ) + + sr, err := l.Search(searchRequest) + if err != nil { + log.Fatal(err) + } + + if len(sr.Entries) > 1 || len(sr.Entries) == 0 { + return "", fmt.Errorf("to many or none entries %d", len(sr.Entries)) + } + + entry := sr.Entries[0] + return entry.GetAttributeValue(keyAttribute), nil +} + +func isPGPMessage(msg string) (bool, error) { + matched, err := regexp.MatchString(`-----BEGIN PGP MESSAGE-----[\s\S]+?-----END PGP MESSAGE-----`, msg) + return matched, err +} + +func isEncrypted(mail *message.Entity) (bool) { + t, _, _ := mail.Header.ContentType() + if strings.ToLower(t) == "multipart/encrypted" { + return true + } + + if mail.MultipartReader() == nil { + if b, err := ioutil.ReadAll(mail.Body); err == nil { + enc, _ := isPGPMessage(string(b)) + if enc { + return true + } + } + } + return false +} + +func encryptEML(eml string, armoredKeyRing *string) { + var b bytes.Buffer + var r, r2 io.Reader + + r = strings.NewReader(eml) + + m, err := message.Read(r) + if err != nil { + log.Fatal(err) + } + + if isEncrypted(m) { + log.Print(eml) + os.Exit(0) + } + + r2 = strings.NewReader(*armoredKeyRing) + entityList, err := openpgp.ReadArmoredKeyRing(r2) + if err != nil { + log.Fatal(err) + } + + // Create a new PGP/MIME writer + var ciphertext struct{ *message.Writer } + cleartext := pgpmime.Encrypt(&ciphertext, nil, entityList, nil, nil) + + // Add the PGP/MIME Content-Type header field to the mail header + m.Header.Set("Content-Type", cleartext.ContentType()) + m.Header.Set( + "X-Encrypted-By", + fmt.Sprintf("%s-v%s", config.Section("").Key("service_name").String(), config.Section("").Key("version").String())) + + // Create a new mail writer with our mail header + mw, err := message.CreateWriter(&b, m.Header) + if err != nil { + log.Fatal(err) + } + // Set the PGP/MIME writer output to the mail body + ciphertext.Writer = mw + + // Close all writers + if err := cleartext.Close(); err != nil { + log.Fatal(err) + } + if err := mw.Close(); err != nil { + log.Fatal(err) + } + + log.Println(b.String()) +} + +func main() { + cfg, err := ini.Load("config.ini") + if err != nil { + fmt.Printf("Fail to read file: %v", err) + os.Exit(1) + } + + config = cfg + + argsWithoutProg := os.Args[1:] + + if len(argsWithoutProg) != 1 { + log.Fatal("No recipient as argument") + } + recipient := argsWithoutProg[0] + + fi, err := os.Stdin.Stat() + if err != nil { + log.Fatal(err) + } + if fi.Size() == 0 { + log.Fatal("stdin is empty") + } + + data, err := ioutil.ReadAll(os.Stdin) + rawEml := string(data) + + armoredKeyRing, err := getArmoredKeyRing(&recipient) + if err != nil { + log.Fatal(err) + } + + encryptEML(rawEml, &armoredKeyRing) + os.Exit(0) +} diff --git a/ldap.schema b/ldap.schema new file mode 100644 index 0000000..0b053d5 --- /dev/null +++ b/ldap.schema @@ -0,0 +1,40 @@ +attributeType ( 1.3.6.1.4.1.3401.8.2.11 NAME 'pgpKey' + DESC 'pgpKey attribute for PGP' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE + X-ORIGIN 'Pretty Good Privacy (PGP)' ) +attributeType ( 1.3.6.1.4.1.3401.8.2.13 NAME 'pgpEnabled' + DESC 'pgpDisabled attribute for PGP' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE + X-ORIGIN 'Pretty Good Privacy (PGP)' ) +attributeType ( 1.3.6.1.4.1.3401.8.2.14 NAME 'pgpKeyID' + DESC 'pgpKeyID attribute for PGP' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE + X-ORIGIN 'Pretty Good Privacy (PGP)' ) +attributeType ( 1.3.6.1.4.1.3401.8.2.15 NAME 'pgpKeyType' + DESC 'pgpKeyType attribute for PGP' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE + X-ORIGIN 'Pretty Good Privacy (PGP)' ) +attributeType ( 1.3.6.1.4.1.3401.8.2.17 NAME 'pgpKeyCreateTime' + DESC 'pgpKeyCreateTime attribute for PGP' + EQUALITY caseIgnoreMatch + ORDERING caseIgnoreOrderingMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + SINGLE-VALUE + X-ORIGIN 'Pretty Good Privacy (PGP)' ) + +objectClass ( 1.3.6.1.4.1.3401.8.2.24 NAME 'pgpKeyInfo' + DESC 'PGP defined objectclass to maintain information about a PGP key' + SUP top AUXILIARY + MUST ( pgpKey ) + MAY ( pgpEnabled $ pgpKeyID $ pgpKeyType $ pgpKeyCreateTime ) + X-ORIGIN 'Pretty Good Privacy (PGP)' ) diff --git a/sample.mail b/sample.mail new file mode 100644 index 0000000..80c5ff2 --- /dev/null +++ b/sample.mail @@ -0,0 +1,82 @@ +Return-Path: +Delivered-To: nico@example.com +X-Spam-Flag: NO +X-Spam-Score: -2.899 +X-Spam-Level: +MIME-Version: 1.0 +Date: Sun, 03 Nov 2019 20:34:55 +0000 +Content-Type: multipart/alternative; + boundary="--=_RainLoop_447_783840555.1572813295" +From: "=?utf-8?B?U2Now7x0cnVtcGYsIE5pa2xhcw==?=" +Message-ID: <7ba96fec1371da1611054b67982dec34@example.org> +Subject: Lorem Ipsum +To: nico@example.com + + +----=_RainLoop_447_783840555.1572813295 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy = +eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam = +voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet c= +lita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit am= +et. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam non= +umy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed d= +iam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. St= +et clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor si= +t amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam= + nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, s= +ed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum= +. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolo= +r sit amet. =0A=0ADuis autem vel eum iriure dolor in hendrerit in vulputa= +te velit esse molestie consequat, vel illum dolore eu feugiat nulla facil= +isis at vero eros et accumsan et iusto odio dignissim qui blandit praesen= +t luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lor= +em ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy n= +ibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. =0A= +=0AUt wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper s= +uscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel = +eum iriure dolor in hendrerit in vulputate velit esse molestie consequat,= + vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et = +iusto odio dignissim qui blandit praesent luptatum zzril delenit augue du= +is dolore te feugait nulla facilisi. =0A * Lorem =0A * Ipsum =0A * dolor + +----=_RainLoop_447_783840555.1572813295 +Content-Type: text/html; charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +
= +
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam non= +umy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed d= +iam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. St= +et clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor si= +t amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam= + nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam era= +t, sed diam voluptua. At vero eos et accusam et justo duo dolores et= + ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem i= +psum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing el= +itr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna ali= +quyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolore= +s et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lor= +em ipsum dolor sit amet.

Duis autem vel eum iriure dolor in hendr= +erit in vulputate velit esse molestie consequat, vel illum dolore eu feug= +iat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui = +blandit praesent luptatum zzril delenit a= +ugue duis dolore te feugait nulla facilisi. Lorem ipsum= + dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euis= +mod tincidunt ut laoreet dolore magna aliquam erat volutpat.

Ut wisi enim ad minim veniam, quis = +nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea= + commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulpu= +tate velit esse molestie consequat, vel illum dolore eu feugiat nulla fac= +ilisis at vero eros et accumsan et iusto odio dignissim qui blandit praes= +ent luptatum zzril delenit augue duis dolore te feugait nu= +lla facilisi.
  1. Lorem
  2. Ipsum
  3. dolor
= +
+ +----=_RainLoop_447_783840555.1572813295-- diff --git a/sample_encrypted.mail b/sample_encrypted.mail new file mode 100644 index 0000000..a32983f --- /dev/null +++ b/sample_encrypted.mail @@ -0,0 +1,41 @@ +Return-Path: +Delivered-To: nico@example.com +X-Spam-Flag: NO +X-Spam-Score: -2.899 +X-Spam-Level: +MIME-Version: 1.0 +Date: Sun, 03 Nov 2019 20:57:20 +0000 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: quoted-printable +From: "=?utf-8?B?U2Now7x0cnVtcGYsIE5pa2xhcw==?=" +Message-ID: <1c3ebfd9ea9ac4c83c9420823d0f1c71@example.org> +Subject: Lorem Ipsum Encrypted +To: nico@example.com +X-Spamd-Bar: / + +-----BEGIN PGP MESSAGE-----=0AVersion: OpenPGP.js v2.6.2=0AComment: https= +://openpgpjs.org=0A=0AwcFMA6Ac9Cn7YTymARAAgCXEZ0JUljhiFQPtIAoNIqDR638zhaT= +eDvIF4oMM=0AXJp8to55bx5RcKWPjbxKjKj5fVw7dd/02pjUgP/podp/JIpaOcnDjswj01uZ= +=0AQ+oKZbNZ7A2PEldu5uZSgBS964YW/01W4Of0XX/9j4gc9j80D18GSuD5APpn=0AOB/PDlr= +hQ2ndCZSH0PoZj6dpT/jZn03QYqmQEkrcSO3y9gCY68p9Sefc+2f8=0AcQF3NTPjyuDrJRa2k= +5G4Yk34wb24wBmYCZkUIB6bOB4eya/ivmJFGbhDTDlk=0Aozczf6Rw01am0/DLpY96LGtB483= +sPie+wdYjFLOeD4CX9kUX9pEUBqwV4sIV=0A/+Xq1cv4CApvfigvP+G7SW1K7UCZv0E4Mxysm= +s8D7ZR9iYrMtogQOf8OeloV=0AN2Ut4HZLVJjnpNBBDdKfwFopoNXXl7zlLG4R9CYVli3xUK2= +0JRV9CbENvv1V=0Ab7KnxY1rhQ+a50tosgZzK61sFtRwHsyW/JagzdWL46gylhnQLh71+VNdI= +aSK=0AvwmtwnKlNc7c7zE6zFWTnZOogzHv4vKKI4h0OTuUXb4hEMS9u9xFkrhVF+LV=0AatG+= +3ShvfPtymGysPWvpQb9uXDZdj+9L0rRFR05DvwrhbZWcSqFQligo882Y=0AhLxlh6P9kKr5kt= +BninOWHazG2IIHjw54JvDpXBddcp7SwfcByU7wUYJg8yYU=0A650GZWxb6RGhMkLow4dBGIGi= +QdOsm4GsXvbyVgxZ9tiWFsFaTVDAsl5Oe3Tn=0A39Js8GTs96VAEx0wNVHr2fSIrpu/Optit+= +ZfjkdudaaEXRKGp4QfU47PsHeA=0APnziq6cLxTJcLSGat+HwUeYklWrrKOp1gFC507Wp4C52= +SRxg8TGWhLVlOaeB=0A7yRIBhVC9JLWS+gl5wD6YPTg/Ft0vZPQjz7pWS34icSrYYVuFT11zV= +CD2W6V=0AqeE9+WLrJkBgqSssx3lNepKsoP938HXhDZh6r608vyllyn6QYxaI/03J21B5=0Ai= +0hWs1bXESHKy753EaE/KeKeJ8VMGtoFgScKk5XNNUOlg37s5BrqBt0Dll+/=0AG0oVbroet+I= +HMrnuUt7Yc8if2c/4+mY1kBfWPvTgEFmQRVDVDNrywFJPbwVn=0ApaQceuqbh19B9uH1hGVoX= +brgCNbNjuwkRAmb8gw9FnT3oWKUZyTctxR9dakk=0AjNuaDZrQE9U63J79LKTXVdYCuaxAdny= +aTy3RTcfdAYGYU+8FPhITSn4nMMqz=0Ai3QrJIo2jSrH1FAelKXwyaG57FRX+oWIb3xvJWdSt= +WUf41M+jBxXYodTlozz=0AzVuFOtROEts+t7UfkZGixbBVjpL3OeLmMIYvOC4CKGR0egCNAqi= +yqRWsrk/K=0A+bCCZQXjY4uKeSjDhorlZ2T/PNQTOkeqwNzRyOjafW+1nBf4pgZSpCaxdXf6= +=0A/PfVHWD2vt5Ung1MgnXZoyqvbVcWjtg0+3SBLtrZI+kuB31Yf8zHRhrPlHW+=0Auxz/fiF= +iCnY9YoFUHys23A/p08SsRf7iuBVPHquQEIdilWhN0xzPV/sPRLFF=0AdZCY65djjw4iSgwpY= +SSaFXDHObCjPq/7F/M+tL/Lga7FsXdM+q9LR8RfAgti=0AZ104/rujVGuTmQ=3D=3D=0A=3D6= +Y+a=0A-----END PGP MESSAGE-----