summaryrefslogtreecommitdiff
path: root/localmksslcerts
blob: bf65432366c19a9be0b48fdb1315de0f3ca4a00a (plain)
  1. #!/bin/sh
  2. #
  3. # /usr/local/sbin/localmksslcerts
  4. # Copyright 2001-2004 Jonas Smedegaard <dr@jones.dk>
  5. #
  6. # $Id: localmksslcerts,v 1.20 2005-10-18 12:32:02 jonas Exp $
  7. #
  8. # Generate certificates for mail (and other) servers
  9. # Based on uw-imapd-ssl post-install script
  10. #
  11. # TODO: Use getopts
  12. # TODO: Add symlink from CA certificate to cacert.pem if non-existing
  13. # TODO: Default CA certificate is cacert.pem if --cacert not set
  14. set -e
  15. prg=$(basename $0)
  16. copyright="(C) 2001-2004 Jonas Smedegaard <dr@jones.dk>"
  17. # Set some defaults
  18. PATH="$PATH:/usr/bin/ssl"
  19. DAYS2EXPIRE="365"
  20. SSLCERTDIR="/etc/ssl/certs"
  21. SSLKEYDIR="/etc/ssl/private"
  22. usage() {
  23. echo "$prg, $copyright
  24. Usage: $prg [--fqdn <FQDN>] [...] --daemon <daemon> [...] [--force]
  25. or: $prg <daemon> [<daemon>...] [-f]
  26. Options:
  27. --fqdn <FQDN> Fully Qualified Domain Name for this host.
  28. --cn <country> Country Name (2 letter code)
  29. --state <state> State or Province Name (full name)
  30. --loc <locality> Locality Name (eg, city)
  31. --org <organisation> Organisation/company
  32. --ou <department> Organisational unit/department
  33. --daemon <daemon> Daemon(s) in need for a certificate
  34. (certificate is generated for each daemon)
  35. --issuer <issuer> Email address of entity issuing certificate
  36. --cert Use certified host certificate
  37. --cacert <file> Where to store host certificate if missing
  38. --makeca Create CA certificate if missing
  39. -f, --force Force overwriting existing certificate(s)
  40. -h, --help This help text
  41. Examples:
  42. Create certs for UW imapd/popd, bound to default host cert if there:
  43. localmksslcerts imapd pop3d
  44. Create host cert for non-default hostname, overwriting stray files:
  45. localmksslcerts --fqdn mail.jones.dk --cert --force
  46. Create host cert for non-default hostname and bind to UW daemons:
  47. localmksslcerts --fqdn mail.jones.dk --cert --force imapd pop3d
  48. Create CA-signed host cert (and CA cert if not there):
  49. localmksslcerts --cert --cacert IT-guide_dr_Jones_CA
  50. If issuer is not given, \"postmaster@<localdomain>\" is used."
  51. exit 1
  52. }
  53. mkcerthash() {
  54. filebase="$1"
  55. filename="$filebase.pem"
  56. certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$filename")"
  57. hashfile="$certhash.0"
  58. ln -sf "$filename" "$SSLCERTDIR/$hashfile"
  59. }
  60. mkselfcert() {
  61. filebase="$1"
  62. domain="$2"
  63. filename="$filebase.pem"
  64. openssl req -new -x509 -nodes \
  65. -days "$DAYS2EXPIRE" \
  66. -keyout "$SSLCERTDIR/$filename" \
  67. -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+
  68. $cn
  69. $state
  70. $loc
  71. $org
  72. $ou
  73. $domain
  74. $issuer
  75. +
  76. mkcerthash "$filebase"
  77. chown root:root "$SSLCERTDIR/$filename"
  78. chmod 0640 "$SSLCERTDIR/$filename"
  79. }
  80. mkkey() {
  81. filebase="$1"
  82. openssl genrsa \
  83. -out "$SSLKEYDIR/$filename"
  84. chown root:root "$SSLKEYDIR/$filename"
  85. chmod 0600 "$SSLKEYDIR/$filename"
  86. }
  87. mkcertreq() {
  88. filebase="$1"
  89. domain="$2"
  90. filename="$filebase.pem"
  91. openssl req -new \
  92. -key "$SSLKEYDIR/$filename" \
  93. -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+
  94. $cn
  95. $state
  96. $loc
  97. $org
  98. $ou
  99. $domain
  100. $issuer
  101. +
  102. chown root:root "$SSLCERTDIR/$filename"
  103. chmod 0640 "$SSLCERTDIR/$filename"
  104. }
  105. mkselfcacert() {
  106. filebase="$1"
  107. domain="$2"
  108. cacert="$3"
  109. filename="$filebase.pem"
  110. reqfilename="$filebase.csr"
  111. cafilename="$cacert.pem"
  112. openssl x509 -req \
  113. -days $DAYS2EXPIRE \
  114. -CA "$SSLCERTDIR/$cafilename" \
  115. -CAkey "$SSLKEYDIR/$cafilename" \
  116. -CAcreateserial \
  117. -in "$SSLCERTDIR/$reqfilename" \
  118. -out "$SSLCERTDIR/$filename"
  119. }
  120. mkcacert() {
  121. filebase="$1"
  122. filename="$filebase.pem"
  123. #FIXME: Make strength configurable
  124. openssl genrsa -des3 \
  125. -out "$SSLKEYDIR/$filename" 1024
  126. chown root:root "$SSLKEYDIR/$filename"
  127. chmod 0400 "$SSLKEYDIR/$filename"
  128. # Generate and pre-fill certification request
  129. #FIXME: Make validity configurable
  130. openssl req -new \
  131. -key "$SSLKEYDIR/$filename" \
  132. -x509 -days 1095 \
  133. -out "$SSLCERTDIR/$filename"
  134. # Add hash to certified public certificate and cleanup
  135. mkcerthash "$cacert"
  136. chown root:root "$SSLCERTDIR/$filename"
  137. chmod 0640 "$SSLCERTDIR/$filename"
  138. }
  139. fqdn=''
  140. cn=''
  141. state=''
  142. loc=''
  143. org=''
  144. ou=''
  145. daemon=''
  146. daemons=''
  147. issuer=''
  148. cert=''
  149. cacert=''
  150. makeca=''
  151. force=''
  152. args=''
  153. TEMP=`getopt -o f --long help,fqdn:,cn:,state:,loc:,org:,ou:,daemon:,issuer:,cert,cacert:,makeca,force -n "$prg" -- "$@"`
  154. # Check for non-GNU getopt
  155. if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
  156. eval set -- "$TEMP"
  157. # TODO: Redesign to a case of selfcert, selfca or ca
  158. while true ; do
  159. case "$1" in
  160. --help) usage;;
  161. --fqdn) fqdn="$2"; shift 2;;
  162. --cn) cn="$2"; shift 2;;
  163. --state) state="$2"; shift 2;;
  164. --loc) loc="$2"; shift 2;;
  165. --org) org="$2"; shift 2;;
  166. --ou) ou="$2"; shift 2;;
  167. --daemon) daemons="$daemons$2 "; shift 2;;
  168. --issuer) issuer="$2"; shift 2;;
  169. --cert) cert=1; shift;;
  170. --cacert) cacert="$2"; shift 2;;
  171. --makeca) makeca=1; shift;;
  172. --force|-f) force=1; shift;;
  173. --) shift; break;;
  174. *) echo "Internal error!" ; exit 1 ;;
  175. esac
  176. done
  177. if [ -z "$issuer" ]; then
  178. DOMAINNAME="`hostname -d`"
  179. ISSUER="postmaster@$DOMAINNAME"
  180. fi
  181. if [ -z "$fqdn" ]; then
  182. if [ $# -gt 0 ]; then
  183. fqdn="`hostname -f`"
  184. else
  185. echo "Too few parameters!"
  186. usage
  187. fi
  188. fi
  189. for val in org ou; do
  190. if eval [ -z "\$$val" ]; then
  191. eval "$val=\"$fqdn\""
  192. fi
  193. done
  194. for val in cn state loc; do
  195. if eval [ -z "\$$val" ]; then
  196. eval "$val=\".\""
  197. fi
  198. done
  199. if [ -n "$cert" ]; then
  200. if [ ! -s "$SSLCERTDIR/$fqdn.pem" ] || [ ! -s "$SSLKEYDIR/$fqdn.pem" ]; then
  201. echo "WARNING: Host certificate for \"$fqdn\" missing..."
  202. if [ -z "$cacert" ]; then
  203. echo "ERROR: The \"--cacert\" option is required when making a host certificate!"
  204. exit 1
  205. fi
  206. # Cleaning up - if allowed
  207. for file in "$SSLKEYDIR/$fqdn.pem" "$SSLCERTDIR/$fqdn.csr" "$SSLCERTDIR/$fqdn.pem"; do
  208. if [ -e "$file" ]; then
  209. if [ -n "$force" ]; then
  210. rm -f "$file"
  211. else
  212. echo "ERROR: File \"$file\" already exists!"
  213. exit 1
  214. fi
  215. fi
  216. done
  217. if [ ! -s "$SSLCERTDIR/$cacert.pem" ] || [ ! -s "$SSLKEYDIR/$cacert.pem" ]; then
  218. echo "WARNING: CAcert (certifying authority certificate) missing..."
  219. if [ -z "$makeca" ]; then
  220. echo "ERROR: The \"--makeca\" option is required when making a CAcert!"
  221. exit 1
  222. fi
  223. # Generate private key for CA certificate
  224. echo "Generating CAcert \"$cacert\"..."
  225. mkcacert "$cacert"
  226. fi
  227. echo "Generating host certificate for \"$fqdn\"..."
  228. # Generate private key for host certificate
  229. mkkey "$fqdn"
  230. # Generate and pre-fill certification request
  231. mkcertreq "$fqdn" "$fqdn"
  232. if [ -n "$cacert" ]; then
  233. # Generate public certificate from certification request
  234. mkselfcacert "$fqdn" "$fqdn" "$cacert"
  235. elif [ ! -f "$SSLCERTDIR/$fqdn.pem" ]; then
  236. echo "Certificate request generated: $SSLCERTDIR/$fqdn.csr"
  237. echo "Now pass the request to you certificate authority, save their"
  238. echo "provided certificate as \"$SSLCERTDIR/$fqdn.pem\","
  239. echo "and run this script with same options again."
  240. exit 0
  241. fi
  242. if [ ! -f "$SSLCERTDIR/$fqdn.pem" ]; then
  243. echo "ERROR: certificate "$SSLCERTDIR/$fqdn.pem" not found!"
  244. exit 1
  245. fi
  246. # Add hash to certified public certificate and cleanup
  247. mkcerthash "$fqdn"
  248. rm "$SSLCERTDIR/$fqdn.csr"
  249. fi
  250. fi
  251. for daemon in $daemons $@; do
  252. if [ -f "$SSLCERTDIR/$daemon.pem" ]; then
  253. if [ -n "$force" ]; then
  254. rm -f "$SSLCERTDIR/$(openssl x509 -noout -hash < "$SSLCERTDIR/$daemon.pem").0"
  255. rm -f "$SSLCERTDIR/$daemon.pem"
  256. else
  257. echo "Ignoring certificate (/etc/ssl/certs/$daemon.pem already exists...)"
  258. continue
  259. fi
  260. fi
  261. if [ -n "$cert" ]; then
  262. echo "Attaching $daemon to certified certificate for $fqdn."
  263. ln -sf "$fqdn.pem" "$SSLCERTDIR/$daemon.pem"
  264. ln -sf "$fqdn.pem" "$SSLKEYDIR/$daemon.pem"
  265. else
  266. echo -n "Generating self-certifying $daemon certificate..."
  267. mkselfcert "$daemon" "$fqdn"
  268. echo "Done!"
  269. fi
  270. done