1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package tls2021import (22 "crypto/tls"2324 "github.com/foxcpp/maddy/framework/config"25 "github.com/foxcpp/maddy/framework/log"26)2728var strVersionsMap = map[string]uint16{29 "tls1.0": tls.VersionTLS10,30 "tls1.1": tls.VersionTLS11,31 "tls1.2": tls.VersionTLS12,32 "tls1.3": tls.VersionTLS13,33 "": 0, // use crypto/tls defaults if value is not specified34}3536var strCiphersMap = map[string]uint16{37 // TLS 1.0 - 1.2 cipher suites.38 "RSA-WITH-RC4128-SHA": tls.TLS_RSA_WITH_RC4_128_SHA,39 "RSA-WITH-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,40 "RSA-WITH-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,41 "RSA-WITH-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,42 "RSA-WITH-AES128-CBC-SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,43 "RSA-WITH-AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,44 "RSA-WITH-AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,45 "ECDHE-ECDSA-WITH-RC4128-SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,46 "ECDHE-ECDSA-WITH-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,47 "ECDHE-ECDSA-WITH-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,48 "ECDHE-RSA-WITH-RC4128-SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,49 "ECDHE-RSA-WITH-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,50 "ECDHE-RSA-WITH-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,51 "ECDHE-RSA-WITH-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,52 "ECDHE-ECDSA-WITH-AES128-CBC-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,53 "ECDHE-RSA-WITH-AES128-CBC-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,54 "ECDHE-RSA-WITH-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,55 "ECDHE-ECDSA-WITH-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,56 "ECDHE-RSA-WITH-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,57 "ECDHE-ECDSA-WITH-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,58 "ECDHE-RSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,59 "ECDHE-ECDSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,60}6162var strCurvesMap = map[string]tls.CurveID{63 "p256": tls.CurveP256,64 "p384": tls.CurveP384,65 "p521": tls.CurveP521,66 "X25519": tls.X25519,67}6869// TLSversionsDirective parses directive with arguments that specify70// minimum and maximum supported TLS versions.71//72// It returns [2]uint16 value for use in corresponding fields from tls.Config.73func TLSVersionsDirective(_ *config.Map, node config.Node) (interface{}, error) {74 switch len(node.Args) {75 case 1:76 value, ok := strVersionsMap[node.Args[0]]77 if !ok {78 return nil, config.NodeErr(node, "invalid TLS version value: %s", node.Args[0])79 }80 return [2]uint16{value, value}, nil81 case 2:82 minValue, ok := strVersionsMap[node.Args[0]]83 if !ok {84 return nil, config.NodeErr(node, "invalid TLS version value: %s", node.Args[0])85 }86 maxValue, ok := strVersionsMap[node.Args[1]]87 if !ok {88 return nil, config.NodeErr(node, "invalid TLS version value: %s", node.Args[1])89 }90 return [2]uint16{minValue, maxValue}, nil91 default:92 return nil, config.NodeErr(node, "expected 1 or 2 arguments")93 }94}9596// TLSCiphersDirective parses directive with arguments that specify97// list of ciphers to offer to clients (or to use for outgoing connections).98//99// It returns list of []uint16 with corresponding cipher IDs.100func TLSCiphersDirective(_ *config.Map, node config.Node) (interface{}, error) {101 if len(node.Args) == 0 {102 return nil, config.NodeErr(node, "expected at least 1 argument, got 0")103 }104105 res := make([]uint16, 0, len(node.Args))106 for _, arg := range node.Args {107 cipherId, ok := strCiphersMap[arg]108 if !ok {109 return nil, config.NodeErr(node, "unknown cipher: %s", arg)110 }111 res = append(res, cipherId)112 }113 log.Debugln("tls: using non-default cipherset:", node.Args)114 return res, nil115}116117// TLSCurvesDirective parses directive with arguments that specify118// elliptic curves to use during TLS key exchange.119//120// It returns []tls.CurveID.121func TLSCurvesDirective(_ *config.Map, node config.Node) (interface{}, error) {122 if len(node.Args) == 0 {123 return nil, config.NodeErr(node, "expected at least 1 argument, got 0")124 }125126 res := make([]tls.CurveID, 0, len(node.Args))127 for _, arg := range node.Args {128 curveId, ok := strCurvesMap[arg]129 if !ok {130 return nil, config.NodeErr(node, "unknown curve: %s", arg)131 }132 res = append(res, curveId)133 }134 log.Debugln("tls: using non-default curve preferences:", node.Args)135 return res, nil136}