summaryrefslogtreecommitdiff
path: root/localmksslcerts
blob: 742a04aab68cf84ac2afc882d68fba410bc5cd77 (plain)
  1. #!/bin/sh
  2. #
  3. # /usr/local/sbin/localmksslcerts
  4. # Copyright 2001-2005 Jonas Smedegaard <dr@jones.dk>
  5. #
  6. # $Id: localmksslcerts,v 1.21 2005-10-19 19:07:19 jonas Exp $
  7. #
  8. # Generate certificates for mail (and other) servers
  9. # Based on uw-imapd-ssl post-install script
  10. #
  11. # TODO: Add symlink from CA certificate to cacert.pem if non-existing
  12. set -e
  13. prg=$(basename $0)
  14. copyright="(C) 2001-2005 Jonas Smedegaard <dr@jones.dk>"
  15. # Set some defaults
  16. PATH="$PATH:/usr/bin/ssl"
  17. DAYS2EXPIRE="365"
  18. SSLCERTDIR="/etc/ssl/certs"
  19. SSLKEYDIR="/etc/ssl/private"
  20. HOSTNAME="$(hostname -f)"
  21. DOMAINNAME="$(hostname -d)"
  22. CN="."
  23. STATE="."
  24. LOC="."
  25. ORG="@@HOSTNAME@@"
  26. OU="@@HOSTNAME@@"
  27. FQDN="@@HOSTNAME@@"
  28. ISSUER="postmaster@@@DOMAINNAME@@"
  29. if [ -f /etc/local/mksslcerts.conf ]; then
  30. . /etc/local/mksslcerts.conf
  31. fi
  32. for var in CN STATE LOC ORG OU FQDN ISSUER; do
  33. eval $var=\"\$\(echo \"\$$var\" \| /bin/sed -e \"s/@@HOSTNAME@@/$HOSTNAME/g\" -e \"s/@@DOMAINNAME@@/$DOMAINNAME/g\"\)\";
  34. done
  35. mkcerthash() {
  36. base="$1"
  37. certfile="$base.pem"
  38. certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$certfile")"
  39. ln -sf "$certfile" "$SSLCERTDIR/$certhash.0"
  40. }
  41. mkselfcert() {
  42. base="$1"
  43. domain="$2"
  44. keypath="$SSLKEYDIR/$base.pem"
  45. certpath="$SSLCERTDIR/$base.pem"
  46. openssl req -new -x509 -nodes \
  47. -days "$DAYS2EXPIRE" \
  48. -keyout "$keypath" \
  49. -out "$certpath" > /dev/null 2>&1 <<+
  50. $cn
  51. $state
  52. $loc
  53. $org
  54. $ou
  55. $domain
  56. $issuer
  57. +
  58. chown root:root "$keypath" "$certpath"
  59. chmod 0600 "$keypath"
  60. chmod 0644 "$certpath"
  61. mkcerthash "$base"
  62. }
  63. mkkey() {
  64. filebase="$1"
  65. keypath="$SSLKEYDIR/$filebase.pem"
  66. openssl genrsa \
  67. -out "$keypath" > /dev/null 2>&1
  68. chown root:root "$keypath"
  69. chmod 0600 "$keypath"
  70. }
  71. mkcertreq() {
  72. base="$1"
  73. domain="$2"
  74. keypath="$SSLKEYDIR/$base.pem"
  75. reqpath="$SSLCERTDIR/$base.csr"
  76. openssl req -new \
  77. -key "$keypath" \
  78. -out "$reqpath" > /dev/null 2>&1 <<+
  79. $cn
  80. $state
  81. $loc
  82. $org
  83. $ou
  84. $domain
  85. $issuer
  86. .
  87. .
  88. +
  89. chown root:root "$reqpath"
  90. chmod 0640 "$reqpath"
  91. }
  92. mkselfcacert() {
  93. base="$1"
  94. domain="$2"
  95. cacert="$3"
  96. certpath="$SSLCERTDIR/$base.pem"
  97. reqpath="$SSLCERTDIR/$base.csr"
  98. cakeypath="$SSLKEYDIR/$cacert.pem"
  99. cacertpath="$SSLCERTDIR/$cacert.pem"
  100. openssl x509 -req \
  101. -days $DAYS2EXPIRE \
  102. -CA "$cacertpath" \
  103. -CAkey "$cakeyfile" \
  104. -CAcreateserial \
  105. -in "$reqpath" \
  106. -out "$certpath"
  107. chown root:root "$certpath"
  108. chmod 0644 "$certpath"
  109. mkcerthash "$base"
  110. }
  111. mkcacert() {
  112. base="$1"
  113. cakeypath="$SSLKEYDIR/$base.pem"
  114. cacertpath="$SSLCERTDIR/$base.pem"
  115. #FIXME: Make strength configurable
  116. openssl genrsa -des3 \
  117. -out "$cakeypath" 1024
  118. chown root:root "$cakeypath"
  119. chmod 0400 "$cakeypath"
  120. # Generate and pre-fill certification request
  121. #FIXME: Make validity configurable
  122. openssl req -new \
  123. -key "$cakeypath" \
  124. -x509 -days 1095 \
  125. -out "$cacertpath"
  126. # Add hash to certified public certificate and cleanup
  127. chown root:root "$cacertpath"
  128. chmod 0640 "$cacertpath"
  129. mkcerthash "$base"
  130. }
  131. usage() {
  132. cat <<EOF
  133. $prg, $copyright
  134. Usage: $prg [--type selfcert|ca|selfca] [...] --daemon <daemon> [...] [--force]
  135. or: $prg <daemon> [<daemon>...] [-f]
  136. General options:
  137. --type <CERT_TYPE> Type of certificate
  138. selfcert Self-certified certificate
  139. ca Use Certificate Authority
  140. selfca Homemade Certificate Authority
  141. Default: try guess based on other options
  142. --daemon <daemon> Daemon(s) in need for a certificate
  143. Default: none (or create only CA cert)
  144. (selfcert certs are unique per daemon)
  145. (ca and selfca are shared per hostname)
  146. -f, --force Force overwriting existing certificate(s)
  147. -h, --help This help text
  148. General certificate options:
  149. --fqdn <FQDN> Fully Qualified Domain Name
  150. Default: $FQDN
  151. --cn <country> Country Name (2 letter code)
  152. Default: $CN
  153. --state <state> State or Province Name (full name)
  154. Default: $STATE
  155. --loc <locality> Locality Name (eg, city)
  156. Default: $LOC
  157. --org <organisation> Organisation/company
  158. Default: $ORG
  159. --ou <department> Organisational unit/department
  160. Default: $OU
  161. --issuer <issuer> Email address of entity issuing certificate
  162. Default: $ISSUER
  163. Selfcert options:
  164. --cacert <file> Certificate Authority to use/create
  165. Default: \"cacert\" (possibly symlinked)
  166. --makeca Create CA certificate if missing
  167. Examples:
  168. Create certs for UW imapd/popd, guessing hostname and certificate type:
  169. $prg imapd pop3d
  170. Create host cert for non-default hostname and bind to UW daemons:
  171. $prg --type selfca --fqdn mail.$DOMAINNAME imapd pop3d
  172. Create local CA, signed host cert for $HOSTNAME and links to UW daemons:
  173. $prg --type selfca --cacert My-own_CA --makeca imapd pop3d
  174. Create certificate request to pass to Certificate Authority
  175. $prg --type ca --fqdn mail.$DOMAINNAME
  176. Finish install of CA-certified host certificate
  177. $prg --type ca --fqdn mail.$DOMAINNAME imapd pop3d
  178. EOF
  179. exit 1
  180. }
  181. certtype=''
  182. fqdn=''
  183. cn=''
  184. state=''
  185. loc=''
  186. org=''
  187. ou=''
  188. daemon=''
  189. daemons=''
  190. issuer=''
  191. cacert=''
  192. makeca=''
  193. force=''
  194. TEMP=`getopt -o fh --long type:,daemon:,force,help,fqdn:,cn:,state:,loc:,org:,ou:,issuer:,cacert:,makeca -n "$prg" -- "$@"`
  195. # Check for non-GNU getopt
  196. if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
  197. eval set -- "$TEMP"
  198. # TODO: Redesign to a case of selfcert, selfca or ca
  199. while true ; do
  200. case "$1" in
  201. --type) case "$2" in
  202. selfcert|ca|selfca) certtype="$2"; shift 2;;
  203. *) echo "ERROR: Wrong certificate type: \"$2\"!" >&2; usage;;
  204. esac;;
  205. --daemon) daemons="$daemons$2 "; shift 2;;
  206. --force|-f) force=1; shift;;
  207. --help|-h) usage;;
  208. --fqdn) fqdn="$2"; shift 2;;
  209. --cn) cn="$2"; shift 2;;
  210. --state) state="$2"; shift 2;;
  211. --loc) loc="$2"; shift 2;;
  212. --org) org="$2"; shift 2;;
  213. --ou) ou="$2"; shift 2;;
  214. --issuer) issuer="$2"; shift 2;;
  215. --cacert) cacert="$2"; shift 2;;
  216. --makeca) makeca=1; shift;;
  217. --) shift; break;;
  218. *) echo "Internal error!" >&2 ; exit 1 ;;
  219. esac
  220. done
  221. # Non-opt arguments are daemons
  222. daemons="$daemons $@"
  223. # Try to pick a sane certificate type if not explicitly set
  224. if [ -z "$certtype" ] && [ -n "$makeca" ]; then
  225. certtype="selfca"
  226. fi
  227. if [ -z "$certtype" ] && [ -n "$cacert" ] && [ -r "$SSLKEYDIR/$cacert.pem" ]; then
  228. certtype="selfca"
  229. fi
  230. if [ -z "$certtype" ]; then
  231. echo "ERROR: Cannot guess certificate type!" >&2
  232. echo " Please explicitly set --type (or more other options)." >&2
  233. exit 1
  234. fi
  235. # Use defaults for empty vars, and export
  236. cn="${cn:-$CN}"
  237. state="${state:-$STATE}"
  238. loc="${loc:-$LOC}"
  239. org="${org:-$ORG}"
  240. ou="${ou:-$OU}"
  241. fqdn="${fqdn:-$FQDN}"
  242. issuer="${issuer:-$ISSUER}"
  243. #
  244. case "$certtype" in
  245. selfcert)
  246. ;;
  247. ca)
  248. if [ ! -f "$SSLCERTDIR/$fqdn.pem" ] || [ -n "$force" ]; then
  249. echo "Generating host key for \"$fqdn\"..."
  250. mkkey "$fqdn"
  251. echo "Generating certificate request for \"$fqdn\"..."
  252. mkcertreq "$fqdn" "$fqdn"
  253. echo "Certificate request generated: $SSLCERTDIR/$fqdn.csr!"
  254. echo
  255. echo "Now pass this request to you certificate authority, save their"
  256. echo "provided certificate as \"$SSLCERTDIR/$fqdn.pem\","
  257. echo "and run this script again with same options (except --force)."
  258. exit 0
  259. else
  260. echo "Hashing new certificate and cleanup..."
  261. mkcerthash "$fqdn"
  262. rm -f "$SSLCERTDIR/$fqdn.csr"
  263. fi
  264. ;;
  265. selfca)
  266. if [ -z "$cacert" ] || [ "$cacert" = "cacert" ]; then
  267. if ! cacert="$(basename "$(readlink "$SSLCERTDIR/cacert.pem")")"; then #" mc syntax hilite bug workaround
  268. echo "ERROR: Cannot resolve default CA cert \"$SSLCERTDIR/cacert.pem\"" >&2
  269. exit 1
  270. fi
  271. fi
  272. if [ ! -s "$SSLCERTDIR/$fqdn.pem" ] || [ ! -s "$SSLKEYDIR/$fqdn.pem" ]; then
  273. if [ -n "$force" ]; then
  274. echo "WARNING: Host certificate missing - will be (re)created!" >&2
  275. else
  276. echo "Error: Host certificate is missing!" >&2
  277. echo " Use \"--cacert\" to (re)create." >&2
  278. exit 1
  279. fi
  280. fi
  281. # Cleaning up - if allowed
  282. for file in "$SSLKEYDIR/$fqdn.pem" "$SSLCERTDIR/$fqdn.csr" "$SSLCERTDIR/$fqdn.pem"; do
  283. if [ -n "$force" ] && [ -e "$file" ]; then
  284. rm -f "$file"
  285. else
  286. echo "ERROR: File \"$file\" already exists!" >&2
  287. echo " Use \"--force\" to override." >&2
  288. exit 1
  289. fi
  290. done
  291. if [ ! -s "$SSLCERTDIR/$cacert.pem" ] || [ ! -s "$SSLKEYDIR/$cacert.pem" ]; then
  292. if [ -z "$makeca"]; then
  293. echo "Error: CA certificate is missing!" >&2
  294. echo " Use another \"--cacert\" or create with \"--makeca\"." >&2
  295. exit 1
  296. fi
  297. echo "Generating CAcert \"$cacert\"..."
  298. mkcacert "$cacert"
  299. fi
  300. echo "Generating host key for \"$fqdn\"..."
  301. mkkey "$fqdn"
  302. echo "Generating host certificate for \"$fqdn\"..."
  303. mkcertreq "$fqdn" "$fqdn"
  304. mkselfcacert "$fqdn" "$fqdn" "$cacert"
  305. rm "$SSLCERTDIR/$fqdn.csr"
  306. ;;
  307. *)
  308. echo "Internal error!" >&2
  309. exit 1
  310. ;;
  311. esac
  312. for daemon in $daemons; do
  313. if [ -f "$SSLCERTDIR/$daemon.pem" ]; then
  314. if [ -n "$force" ]; then
  315. echo "WARNING: Removing exisitng certificate \"/etc/ssl/certs/$daemon.pem\"." >&2
  316. rm -f "$SSLCERTDIR/$(openssl x509 -noout -hash < "$SSLCERTDIR/$daemon.pem").0"
  317. rm -f "$SSLCERTDIR/$daemon.pem"
  318. else
  319. echo "WARNING: Ignoring already existing certificate \"/etc/ssl/certs/$daemon.pem\"." >&2
  320. continue
  321. fi
  322. fi
  323. case "$certtype" in
  324. selfcert)
  325. echo -n "Generating self-certifying $daemon certificate..."
  326. mkselfcert "$daemon" "$fqdn"
  327. echo "Done!"
  328. ;;
  329. ca|selfca)
  330. echo "Attaching $daemon to certified certificate for $fqdn."
  331. ln -sf "$fqdn.pem" "$SSLCERTDIR/$daemon.pem"
  332. ln -sf "$fqdn.pem" "$SSLKEYDIR/$daemon.pem"
  333. ;;
  334. *)
  335. echo "Internal error!" >&2
  336. exit 1
  337. ;;
  338. esac
  339. done