Various scripts in various languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ssl_mgmt 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. #!/bin/sh
  2. #
  3. # ssl_mgmt, Copyright © 2012 Thomas Preud'homme
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. # email_account is a helper to create e-mail accounts in a configuration
  19. # using Postfix mail transport agent with vhosts and Cyrus IMAP server,
  20. # in a Debian environment.
  21. set -u
  22. # Display usage.
  23. usage ()
  24. {
  25. local - progname
  26. progname=$1
  27. echo "Usage :"
  28. echo
  29. echo "$progname [-c | -g] renew { <service> | <certificate file> }"
  30. echo "$progname -h"
  31. echo
  32. echo "First form renew the certificate specified as a file or a service name"
  33. echo
  34. echo "Possible option:"
  35. echo
  36. echo "-c Only generate the configuration"
  37. echo "-g Stop after generating the certificate and keys: do not overwrite"
  38. echo " existing ones"
  39. echo
  40. echo "Second form prints this help."
  41. }
  42. # @param question Question to ask
  43. # @return 0 if answer is positive, 1 else
  44. #
  45. # Ask question to the user with a default choice set to no.
  46. ask_user_default_no ()
  47. {
  48. local - answer
  49. answer="unset"
  50. echo -n "$1 [y/N] "
  51. while [ -n "$answer" -a "$answer" != "y" -a "$answer" != "Y" -a "$answer" != "n" -a "$answer" != "N" ]
  52. do
  53. read answer
  54. if [ -z "$answer" -o "$answer" = "n" -o "$answer" = "N" ]
  55. then
  56. return 1
  57. fi
  58. if [ "$answer" = "y" -o "$answer" = "Y" ]
  59. then
  60. break
  61. fi
  62. echo -n "$2 [y/N] "
  63. done
  64. return 0
  65. }
  66. # Parse arguments and test their number and value is correct.
  67. parse_args ()
  68. {
  69. local - user domain action
  70. config_only=""
  71. no_overwrite=""
  72. while getopts "cgh" opt
  73. do
  74. case $opt in
  75. "c")
  76. config_only=yes ;;
  77. "g")
  78. no_overwrite=yes ;;
  79. "h")
  80. if [ $# -gt 1 ]
  81. then
  82. echo "Error! Too many arguments." >&2
  83. exit 1
  84. fi
  85. usage $(basename "$0")
  86. exit 0 ;;
  87. esac
  88. done
  89. eval action="\${$OPTIND:-}"
  90. if [ $(($#-$OPTIND+1)) -ne 2 -o "$action" != "renew" ]
  91. then
  92. usage $(basename "$0")
  93. exit 0
  94. fi
  95. eval service="\$$((OPTIND+1))"
  96. }
  97. # @param file the file we wish to access
  98. # @param mode the mode we wish to access the file in.
  99. # It must be either "READ" or "WRITE".
  100. #
  101. # Exit if we are unable to access the given file with requested access mode
  102. # NB: this function does not return.
  103. exit_if_no_access ()
  104. {
  105. accessedFile="$1"
  106. accessMode="$2"
  107. case $accessMode in
  108. "READ")
  109. [ -r $accessedFile ];;
  110. "WRITE")
  111. [ -w $accessedFile ];;
  112. esac
  113. if [ ! $? -eq 0 ]
  114. then
  115. echo "You do not have enough rights to access ${accessedFile}."
  116. echo "Permission of $accessedFile are:"
  117. getfacl "$accessedFile"
  118. exit 1
  119. fi
  120. }
  121. # Set all variables configuring the overall behavior of ssl_mgmt. A default
  122. # value is provided and overriden if set in the configuration file
  123. set_variables ()
  124. {
  125. cnfFilePath=${cnfFilePath:-/etc/${0##*/}.conf}
  126. exit_if_no_access "$cnfFilePath" "READ"
  127. . $cnfFilePath
  128. workDir=${workDir:-${0%/*/*}/lib/${0##*/}}
  129. csrSubdir=${csrSubdir:-csr}
  130. certSubdir=${certSubdir:-newcerts}
  131. keySubdir=${keySubdir:-newkeys}
  132. certDestDir=${certDestDir:-/etc/ssl/certs}
  133. keyDestDir=${keyDestDir:-/etc/ssl/private}
  134. CACertPath=${CACertPath:-$certDestDir/ca-cert.pem}
  135. CAKeyPath=${CAKeyPath:-$keyDestDir/ca-key.pem}
  136. opensslCnfFile=openssl.cnf
  137. if [ -z "${rootCAPwdPath:-}" ]
  138. then
  139. echo -n "You must set rootCAPwdPath to the file containing" >&2
  140. echo " the root CA password" >&2
  141. fi
  142. managedCerts=${managedCerts:-}
  143. notifiedUsers=${notifiedUsers:-}
  144. if [ -n "${notifiedUsers}" -a -z "${keyId:-}" ]
  145. then
  146. echo -n "You must set keyId to the ID of the key to sign" >&2
  147. echo " the message sent to users to be" >&2
  148. echo "notified of new certificate." >&2
  149. fi
  150. notifySubject=${notifySubject:-'New fingerprint for service $service'}
  151. if [ -z "${notifyTemplate:-}" ]
  152. then
  153. notifyTemplate='Certificate for $service has changed.
  154. The fingerprint of the new certificate is:
  155. $fingerprint'
  156. fi
  157. }
  158. # @param subject the subject line
  159. # @param field the field name
  160. #
  161. # Get a subject field value from the subject line
  162. get_field_from_line ()
  163. {
  164. local - line field result
  165. line="$1"
  166. field="$2"
  167. result="$(echo ${line} | sed -r "s/.*${field} *= *//")"
  168. if [ "$result" != "$line" ]
  169. then
  170. echo "${result%%,*}"
  171. fi
  172. }
  173. # @param certPath the absolute path to the certificate to renew
  174. #
  175. # Get configuration values to fill openssl.cnf with
  176. get_cert_params ()
  177. {
  178. local - subject issuer dates ext fromDate toDate certPath
  179. certPath="$1"
  180. subject="$(openssl x509 -in "$certPath" -noout -subject)"
  181. dates="$(openssl x509 -in "$certPath" -noout -dates)"
  182. exclNoExt="-certopt no_header,no_version,no_serial,no_signame"
  183. exclNoExt="$exclNoExt,no_validity,no_subject,no_issuer,no_pubkey"
  184. exclNoExt="$exclNoExt,no_sigdump,no_aux"
  185. altName="$(openssl x509 -in "$certPath" -text $exclNoExt | while read ext
  186. do
  187. if [ "$ext" = "X509v3 Subject Alternative Name: " ]
  188. then
  189. read altName
  190. echo $altName
  191. break
  192. fi
  193. done)"
  194. country=$(get_field_from_line "$subject" "C")
  195. state=$(get_field_from_line "$subject" "ST")
  196. city=$(get_field_from_line "$subject" "L")
  197. organization=$(get_field_from_line "$subject" "O")
  198. unit=$(get_field_from_line "$subject" "OU")
  199. commonName=$(get_field_from_line "$subject" "CN")
  200. fromDate=${dates#*notBefore=}
  201. fromDate=${fromDate%notAfter*}
  202. fromDate=$(date -d "$fromDate" "+%s")
  203. toDate=${dates#*notAfter=}
  204. toDate=$(date -d "$toDate" "+%s")
  205. days=$(($toDate-$fromDate))
  206. days=$(($days/86400))
  207. }
  208. # @param cmd the current sed replace command
  209. # @param key the pattern to be replaced
  210. # @param value the value to replace the pattern by
  211. #
  212. # Add a replace command s/key/value to the sed replace command passed in
  213. # argument
  214. add_to_replace_cmd ()
  215. {
  216. local - replaceCmd key value
  217. replaceCmd="$1"
  218. key="$2"
  219. value="$3"
  220. echo "$replaceCmd${replaceCmd:+;}s/$key/${value:-}/"
  221. }
  222. # Generate the openssl.cnf configuration file from the openssl.cnf.in template
  223. generate_config ()
  224. {
  225. local - replaceCmd cnfTmpFile
  226. replaceCmd="$(add_to_replace_cmd "${replaceCmd:-}" "@LENGTH@" "${days:-}")"
  227. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@ORG@" "${organization:-}")"
  228. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@ORGUNIT@" "${unit:-}")"
  229. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@LOCALITY@" "${city:-}")"
  230. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@STATE@" "${state:-}")"
  231. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@COUNTRY@" "${country:-}")"
  232. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@COMMONNAME@" "${commonName:-}")"
  233. replaceCmd="$(add_to_replace_cmd "$replaceCmd" "@ALTNAME@" "${altName:-}")"
  234. replaceCmd="$replaceCmd${replaceCmd:+;}s/\(.*=[[:blank:]]*\$\)/#\\1/"
  235. opensslCnfTmpFile="$(mktemp --tmpdir=. openssl.cnf.XXXXXXXXXX)"
  236. sed "$replaceCmd" $opensslCnfFile.in > $opensslCnfTmpFile
  237. if ask_user_default_no "Do you want to edit the openssl configuration file?"
  238. then
  239. if [ -n "${EDITOR:-}" ]
  240. then
  241. $EDITOR $opensslCnfTmpFile
  242. else
  243. editor $opensslCnfTmpFile
  244. fi
  245. fi
  246. mv $opensslCnfTmpFile $opensslCnfFile
  247. }
  248. # @param service the name of the service associated with the certificate to
  249. # renew
  250. # @param certPath the absolute path to the certificate to renew
  251. # @param keyPath the absolute path to the key associated with the certificate
  252. # to renew
  253. #
  254. # Generate the certificate, key and combine key+certificate based on the values
  255. # of the existing certificate
  256. generate_cert ()
  257. {
  258. local - service certPath keyPath reqFile certFile keyFile keycertFile
  259. service="$1"
  260. certPath="$2"
  261. keyPath="$3"
  262. reqFile=${service}-req.pem
  263. certFile=${certPath##*/}
  264. keyFile=${keyPath##*/}
  265. keycertFile=${service}-keycert.pem
  266. keycertPath=${keyPath%/*}/$keycertFile
  267. # Create the CSR and the key
  268. openssl req -new -nodes -out $csrSubdir/$reqFile -keyout $keySubdir/$keyFile -config $opensslCnfFile
  269. if ! openssl req -in $csrSubdir/$reqFile -text -verify -noout 2>/dev/null
  270. then
  271. echo "Generated CSR is corrupted." >&2
  272. rm $csrSubdir/$reqFile $keySubdir/$keyFile
  273. return 1
  274. fi
  275. if ! ask_user_default_no "Is the Certificate Signing Request correct?"
  276. then
  277. return 1
  278. fi
  279. # Sets ownership and access rights of the key
  280. getfacl "$keyPath" | setfacl --set-file=- $keySubdir/$keyFile
  281. chown --reference="$keyPath" $keySubdir/$keyFile
  282. # Sign the CSR to make a certificate
  283. openssl ca -batch -config $opensslCnfFile -cert $CACertPath \
  284. -keyfile $CAKeyPath -passin file:$rootCAPwdPath \
  285. -out $certSubdir/$certFile -infiles $csrSubdir/$reqFile
  286. # Create the keycert file (file with merged key and certificate) and
  287. # sets its ownership and access rights
  288. cat $keySubdir/$keyFile $certSubdir/$certFile > $keySubdir/$keycertFile
  289. getfacl "$keycertPath" | setfacl --set-file=- $keySubdir/$keycertFile
  290. chown --reference="$keycertPath" $keySubdir/$keycertFile
  291. # Safety check
  292. if ! openssl x509 -noout -text -in $certSubdir/$certFile >/dev/null 2>&1 ||
  293. ! openssl verify -CAfile $CACertPath $certSubdir/$certFile >/dev/null 2>&1
  294. then
  295. echo "Generated certificate is corrupted." >&2
  296. rm $certSubdir/$certFile $keySubdir/$keyFile $keySubdir/$keycertFile
  297. return 1
  298. fi
  299. if ! openssl rsa -noout -text -in $keySubdir/$keyFile >/dev/null 2>&1
  300. then
  301. echo "Generated key is corrupted." >&2
  302. rm $certSubdir/$certFile $keySubdir/$keyFile
  303. return 1
  304. fi
  305. certModulus=$(openssl x509 -noout -modulus -in $certSubdir/$certFile)
  306. keyModulus=$(openssl rsa -noout -modulus -in $keySubdir/$keyFile)
  307. if [ -z "$certModulus" -o "$certModulus" != "$keyModulus" ]
  308. then
  309. echo -n "Generated certificate and key do not match." >&2
  310. echo " Aborting." >&2
  311. rm $certSubdir/$certFile $keySubdir/$keyFile $keySubdir/$keycertFile
  312. return 1
  313. fi
  314. # Sets ownership and access rights of the certificate
  315. getfacl "$certPath" | setfacl --set-file=- $certSubdir/$certFile
  316. chown --reference="$certPath" $certSubdir/$certFile
  317. # Notify and install the new certificate
  318. if [ -z "$no_overwrite" ]
  319. then
  320. if [ ! -f "$certDestDir/$certFile" ]
  321. then
  322. echo "No file named $certFile in directory $certDestDir:" >&2
  323. echo "there might be a problem" >&2
  324. fi
  325. if [ ! -f "$keyDestDir/$keyFile" ]
  326. then
  327. echo "Error! No file named $keyFile in directory $keyDestDir:" >&2
  328. echo "there might be a problem." >&2
  329. fi
  330. if [ ! -f "$keyDestDir/$keycertFile" ]
  331. then
  332. echo "Error! No file named $keycertFile in directory $keyDestDir:" >&2
  333. echo "there might be a problem." >&2
  334. fi
  335. mv $keySubdir/$keyFile $keyDestDir
  336. mv $keySubdir/$keycertFile $keyDestDir
  337. mv $certSubdir/$certFile $certDestDir
  338. fingerprint="$(openssl x509 -in "$certPath" -noout -fingerprint)"
  339. fingerprint=${fingerprint#*=}
  340. if [ -n "$notifiedUsers" -a -n "$keyId" ]
  341. then
  342. eval notifySubject="\"$notifySubject\""
  343. eval notifyTemplate="\"$notifyTemplate\""
  344. if [ -z "${keyPwdPath:-}" ]
  345. then
  346. pwdOpt="--passphrase-fd 3"
  347. pwdRedir='3<&0'
  348. else
  349. pwdOpt="--passphrase-file $keyPwdPath"
  350. pwdRedir=""
  351. fi
  352. { gpg -u $keyId --clearsign -a $pwdOpt \
  353. | mail -s "$notifySubject" $notifiedUsers ; } \
  354. 3<&0 <<EOF
  355. $notifyTemplate
  356. EOF
  357. fi
  358. fi
  359. return 0
  360. }
  361. main ()
  362. {
  363. local - ret servicesok certPath keyPath
  364. ret=0
  365. parse_args "$@"
  366. set_variables
  367. cd $workDir
  368. if [ "${service}" = "all" ]
  369. then
  370. if [ -z "$managedCerts" ]
  371. then
  372. echo -n "You need to set managedCerts for renew" >&2
  373. echo " all to work" >&2
  374. fi
  375. exit_if_no_access "$managedCerts" "READ"
  376. services=""
  377. for service in $managedCerts
  378. do
  379. services="$services $service"
  380. done
  381. else
  382. services=${service}
  383. fi
  384. exit_if_no_access "$certDestDir" "WRITE"
  385. exit_if_no_access "$keyDestDir" "WRITE"
  386. for service in $services
  387. do
  388. servicesok=""
  389. certPath="$service"
  390. if [ -f "$certPath" ]
  391. then
  392. service="${service##*/}"
  393. service="${service%.*}"
  394. keyPath="$keyDestDir/${service}.key"
  395. else
  396. certPath="$certDestDir/${service}-cert.pem"
  397. keyPath="$keyDestDir/${service}-key.pem"
  398. fi
  399. if [ ! -f "$certPath" ]
  400. then
  401. ret=1
  402. continue
  403. fi
  404. exit_if_no_access "$certPath" "READ"
  405. exit_if_no_access "$keyPath" "READ"
  406. exit_if_no_access "$rootCAPwdPath" "READ"
  407. get_cert_params "$certPath"
  408. generate_config
  409. if [ -n "$config_only" ]
  410. then
  411. continue
  412. fi
  413. if ! generate_cert "$service" "$certPath" "$keyPath"
  414. then
  415. ret=1
  416. else
  417. servicesok="$servicesok${servicesok:+ }$service"
  418. fi
  419. done
  420. if [ -z "$config_only" ]
  421. then
  422. if [ -n "$servicesok" ]
  423. then
  424. echo "You should restart the following services: $servicesok"
  425. else
  426. echo "No certificate generated"
  427. fi
  428. fi
  429. return $ret
  430. }
  431. main "${@:-""}"
  432. exit $?