ecm.eclass: Set correct KFMIN default for kde-frameworks/*
[gentoo.git] / eclass / ssl-cert.eclass
1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: ssl-cert.eclass
5 # @MAINTAINER:
6 # @AUTHOR:
7 # Max Kalika <max@gentoo.org>
8 # @SUPPORTED_EAPIS: 1 2 3 4 5 6 7
9 # @BLURB: Eclass for SSL certificates
10 # @DESCRIPTION:
11 # This eclass implements a standard installation procedure for installing
12 # self-signed SSL certificates.
13 # @EXAMPLE:
14 # "install_cert /foo/bar" installs ${ROOT}/foo/bar.{key,csr,crt,pem}
15
16 # Guard against unsupported EAPIs.  We need EAPI >= 1 for slot dependencies.
17 case "${EAPI:-0}" in
18         0)
19                 die "${ECLASS}.eclass: EAPI=0 is not supported.  Please upgrade to EAPI >= 1."
20                 ;;
21         1|2|3|4|5|6|7)
22                 ;;
23         *)
24                 die "${ECLASS}.eclass: EAPI=${EAPI} is not supported yet."
25                 ;;
26 esac
27
28 # @ECLASS-VARIABLE: SSL_CERT_MANDATORY
29 # @DESCRIPTION:
30 # Set to non zero if ssl-cert is mandatory for ebuild.
31 : ${SSL_CERT_MANDATORY:=0}
32
33 # @ECLASS-VARIABLE: SSL_CERT_USE
34 # @DESCRIPTION:
35 # Use flag to append dependency to.
36 : ${SSL_CERT_USE:=ssl}
37
38 # @ECLASS-VARIABLE: SSL_DEPS_SKIP
39 # @DESCRIPTION:
40 # Set to non zero to skip adding to DEPEND and IUSE.
41 : ${SSL_DEPS_SKIP:=0}
42
43 if [[ "${SSL_DEPS_SKIP}" == "0" ]]; then
44         if [[ "${SSL_CERT_MANDATORY}" == "0" ]]; then
45                 SSL_DEPEND="${SSL_CERT_USE}? ( || ( dev-libs/openssl:0 dev-libs/libressl:0 ) )"
46                 IUSE="${SSL_CERT_USE}"
47         else
48                 SSL_DEPEND="|| ( dev-libs/openssl:0 dev-libs/libressl:0 )"
49         fi
50
51         case "${EAPI}" in
52                 1|2|3|4|5|6)
53                         DEPEND="${SSL_DEPEND}"
54                 ;;
55                 *)
56                         BDEPEND="${SSL_DEPEND}"
57                 ;;
58         esac
59
60         unset SSL_DEPEND
61 fi
62
63 # @FUNCTION: gen_cnf
64 # @USAGE:
65 # @DESCRIPTION:
66 # Initializes variables and generates the needed
67 # OpenSSL configuration file and a CA serial file
68 #
69 # Access: private
70 gen_cnf() {
71         # Location of the config file
72         SSL_CONF="${T}/${$}ssl.cnf"
73         # Location of the CA serial file
74         SSL_SERIAL="${T}/${$}ca.ser"
75         # Location of some random files OpenSSL can use: don't use
76         # /dev/u?random here -- doesn't work properly on all platforms
77         SSL_RANDOM="${T}/environment:${T}/eclass-debug.log:/etc/resolv.conf"
78
79         # These can be overridden in the ebuild
80         SSL_DAYS="${SSL_DAYS:-730}"
81         SSL_BITS="${SSL_BITS:-4096}"
82         SSL_MD="${SSL_MD:-sha256}"
83         SSL_COUNTRY="${SSL_COUNTRY:-US}"
84         SSL_STATE="${SSL_STATE:-California}"
85         SSL_LOCALITY="${SSL_LOCALITY:-Santa Barbara}"
86         SSL_ORGANIZATION="${SSL_ORGANIZATION:-SSL Server}"
87         SSL_UNIT="${SSL_UNIT:-For Testing Purposes Only}"
88         SSL_COMMONNAME="${SSL_COMMONNAME:-localhost}"
89         SSL_EMAIL="${SSL_EMAIL:-root@localhost}"
90
91         # Create the CA serial file
92         echo "01" > "${SSL_SERIAL}"
93
94         # Create the config file
95         ebegin "Generating OpenSSL configuration${1:+ for CA}"
96         cat <<-EOF > "${SSL_CONF}"
97                 [ req ]
98                 prompt             = no
99                 default_bits       = ${SSL_BITS}
100                 distinguished_name = req_dn
101                 [ req_dn ]
102                 C                  = ${SSL_COUNTRY}
103                 ST                 = ${SSL_STATE}
104                 L                  = ${SSL_LOCALITY}
105                 O                  = ${SSL_ORGANIZATION}
106                 OU                 = ${SSL_UNIT}
107                 CN                 = ${SSL_COMMONNAME}${1:+ CA}
108                 emailAddress       = ${SSL_EMAIL}
109         EOF
110         eend $?
111
112         return $?
113 }
114
115 # @FUNCTION: get_base
116 # @USAGE: [if_ca]
117 # @RETURN: <base path>
118 # @DESCRIPTION:
119 # Simple function to determine whether we're creating
120 # a CA (which should only be done once) or final part
121 #
122 # Access: private
123 get_base() {
124         if [ "${1}" ] ; then
125                 echo "${T}/${$}ca"
126         else
127                 echo "${T}/${$}server"
128         fi
129 }
130
131 # @FUNCTION: gen_key
132 # @USAGE: <base path>
133 # @DESCRIPTION:
134 # Generates an RSA key
135 #
136 # Access: private
137 gen_key() {
138         local base=$(get_base "$1")
139         ebegin "Generating ${SSL_BITS} bit RSA key${1:+ for CA}"
140         if openssl version | grep -i libressl > /dev/null; then
141                 openssl genrsa -out "${base}.key" "${SSL_BITS}" &> /dev/null
142         else
143                 openssl genrsa -rand "${SSL_RANDOM}" \
144                         -out "${base}.key" "${SSL_BITS}" &> /dev/null
145         fi
146         eend $?
147
148         return $?
149 }
150
151 # @FUNCTION: gen_csr
152 # @USAGE: <base path>
153 # @DESCRIPTION:
154 # Generates a certificate signing request using
155 # the key made by gen_key()
156 #
157 # Access: private
158 gen_csr() {
159         local base=$(get_base "$1")
160         ebegin "Generating Certificate Signing Request${1:+ for CA}"
161         openssl req -config "${SSL_CONF}" -new \
162                 -key "${base}.key" -out "${base}.csr" &>/dev/null
163         eend $?
164
165         return $?
166 }
167
168 # @FUNCTION: gen_crt
169 # @USAGE: <base path>
170 # @DESCRIPTION:
171 # Generates either a self-signed CA certificate using
172 # the csr and key made by gen_csr() and gen_key() or
173 # a signed server certificate using the CA cert previously
174 # created by gen_crt()
175 #
176 # Access: private
177 gen_crt() {
178         local base=$(get_base "$1")
179         if [ "${1}" ] ; then
180                 ebegin "Generating self-signed X.509 Certificate for CA"
181                 openssl x509 -extfile "${SSL_CONF}" \
182                         -${SSL_MD} \
183                         -days ${SSL_DAYS} -req -signkey "${base}.key" \
184                         -in "${base}.csr" -out "${base}.crt" &>/dev/null
185         else
186                 local ca=$(get_base 1)
187                 ebegin "Generating authority-signed X.509 Certificate"
188                 openssl x509 -extfile "${SSL_CONF}" \
189                         -days ${SSL_DAYS} -req -CAserial "${SSL_SERIAL}" \
190                         -CAkey "${ca}.key" -CA "${ca}.crt" -${SSL_MD} \
191                         -in "${base}.csr" -out "${base}.crt" &>/dev/null
192         fi
193         eend $?
194
195         return $?
196 }
197
198 # @FUNCTION: gen_pem
199 # @USAGE: <base path>
200 # @DESCRIPTION:
201 # Generates a PEM file by concatinating the key
202 # and cert file created by gen_key() and gen_cert()
203 #
204 # Access: private
205 gen_pem() {
206         local base=$(get_base "$1")
207         ebegin "Generating PEM Certificate"
208         (cat "${base}.key"; echo; cat "${base}.crt") > "${base}.pem"
209         eend $?
210
211         return $?
212 }
213
214 # @FUNCTION: install_cert
215 # @USAGE: <certificates>
216 # @DESCRIPTION:
217 # Uses all the private functions above to generate and install the
218 # requested certificates.
219 # <certificates> are full pathnames relative to ROOT, without extension.
220 #
221 # Example: "install_cert /foo/bar" installs ${ROOT}/foo/bar.{key,csr,crt,pem}
222 #
223 # Access: public
224 install_cert() {
225         if [ $# -lt 1 ] ; then
226                 eerror "At least one argument needed"
227                 return 1;
228         fi
229
230         case ${EBUILD_PHASE} in
231         unpack|prepare|configure|compile|test|install)
232                 die "install_cert cannot be called in ${EBUILD_PHASE}"
233                 ;;
234         esac
235
236         # Generate a CA environment #164601
237         gen_cnf 1 || return 1
238         gen_key 1 || return 1
239         gen_csr 1 || return 1
240         gen_crt 1 || return 1
241         echo
242
243         gen_cnf || return 1
244         echo
245
246         local count=0
247         for cert in "$@" ; do
248                 # Check the requested certificate
249                 if [ -z "${cert##*/}" ] ; then
250                         ewarn "Invalid certification requested, skipping"
251                         continue
252                 fi
253
254                 # Check for previous existence of generated files
255                 for type in key csr crt pem ; do
256                         if [ -e "${ROOT}${cert}.${type}" ] ; then
257                                 ewarn "${ROOT}${cert}.${type}: exists, skipping"
258                                 continue 2
259                         fi
260                 done
261
262                 # Generate the requested files
263                 gen_key || continue
264                 gen_csr || continue
265                 gen_crt || continue
266                 gen_pem || continue
267                 echo
268
269                 # Install the generated files and set sane permissions
270                 local base=$(get_base)
271                 install -d "${ROOT}${cert%/*}"
272                 install -m0400 "${base}.key" "${ROOT}${cert}.key"
273                 install -m0444 "${base}.csr" "${ROOT}${cert}.csr"
274                 install -m0444 "${base}.crt" "${ROOT}${cert}.crt"
275                 install -m0400 "${base}.pem" "${ROOT}${cert}.pem"
276                 : $(( ++count ))
277         done
278
279         # Resulting status
280         if [ ${count} = 0 ] ; then
281                 eerror "No certificates were generated"
282                 return 1
283         elif [ ${count} != ${#} ] ; then
284                 ewarn "Some requested certificates were not generated"
285         fi
286 }