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