summaryrefslogtreecommitdiff
path: root/src/monkeysphere-server
blob: a08007627a323c4ce4c8ae1d0feb204728b61fe0 (plain)
  1. #!/bin/bash
  2. # monkeysphere-server: MonkeySphere server admin tool
  3. #
  4. # The monkeysphere scripts are written by:
  5. # Jameson Rollins <jrollins@fifthhorseman.net>
  6. #
  7. # They are Copyright 2008, and are all released under the GPL, version 3
  8. # or later.
  9. ########################################################################
  10. PGRM=$(basename $0)
  11. SHARE=${SHARE:-"/usr/share/monkeysphere"}
  12. export SHARE
  13. . "${SHARE}/common" || exit 1
  14. VARLIB="/var/lib/monkeysphere"
  15. export VARLIB
  16. # date in UTF format if needed
  17. DATE=$(date -u '+%FT%T')
  18. # unset some environment variables that could screw things up
  19. GREP_OPTIONS=
  20. # default return code
  21. ERR=0
  22. ########################################################################
  23. # FUNCTIONS
  24. ########################################################################
  25. usage() {
  26. cat <<EOF
  27. usage: $PGRM <subcommand> [args]
  28. MonkeySphere server admin tool.
  29. subcommands:
  30. update-users (u) [USER]... update users authorized_keys files
  31. gen-key (g) [HOSTNAME] generate gpg key for the server
  32. show-fingerprint (f) show server's host key fingerprint
  33. publish-key (p) publish server's host key to keyserver
  34. add-certifier (a) KEYID import and tsign a certification key
  35. remove-certifier (r) KEYID remove a certification key
  36. list-certifiers (l) list certification keys
  37. help (h,?) this help
  38. EOF
  39. }
  40. su_monkeysphere_user() {
  41. su --preserve-environment "$MONKEYSPHERE_USER" -- -c "$@"
  42. }
  43. # function to interact with the host gnupg keyring
  44. gpg_host() {
  45. local returnCode
  46. GNUPGHOME="$GNUPGHOME_HOST"
  47. export GNUPGHOME
  48. # NOTE: we supress this warning because we need the monkeysphere
  49. # user to be able to read the host pubring. we realize this might
  50. # be problematic, but it's the simplest solution, without too much
  51. # loss of security.
  52. gpg --no-permission-warning "$@"
  53. returnCode="$?"
  54. # always reset the permissions on the host pubring so that the
  55. # monkeysphere user can read the trust signatures
  56. chgrp "$MONKEYSPHERE_USER" "${GNUPGHOME_HOST}/pubring.gpg"
  57. chmod g+r "${GNUPGHOME_HOST}/pubring.gpg"
  58. return "$returnCode"
  59. }
  60. # function to interact with the authentication gnupg keyring
  61. gpg_authentication() {
  62. GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
  63. export GNUPGHOME
  64. su_monkeysphere_user "gpg $@"
  65. }
  66. # update authorized_keys for users
  67. update_users() {
  68. if [ "$1" ] ; then
  69. # get users from command line
  70. unames="$@"
  71. else
  72. # or just look at all users if none specified
  73. unames=$(getent passwd | cut -d: -f1)
  74. fi
  75. # set mode
  76. MODE="authorized_keys"
  77. # set gnupg home
  78. GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
  79. # check to see if the gpg trust database has been initialized
  80. if [ ! -s "${GNUPGHOME}/trustdb.gpg" ] ; then
  81. failure "GNUPG trust database uninitialized. Please see MONKEYSPHERE-SERVER(8)."
  82. fi
  83. # make sure the authorized_keys directory exists
  84. mkdir -p "${VARLIB}/authorized_keys"
  85. # loop over users
  86. for uname in $unames ; do
  87. # check all specified users exist
  88. if ! getent passwd "$uname" >/dev/null ; then
  89. error "----- unknown user '$uname' -----"
  90. continue
  91. fi
  92. # set authorized_user_ids and raw authorized_keys variables,
  93. # translating ssh-style path variables
  94. authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS")
  95. rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS")
  96. # if neither is found, skip user
  97. if [ ! -s "$authorizedUserIDs" ] ; then
  98. if [ "$rawAuthorizedKeys" = '-' -o ! -s "$rawAuthorizedKeys" ] ; then
  99. continue
  100. fi
  101. fi
  102. log "----- user: $uname -----"
  103. # make temporary directory
  104. TMPDIR=$(mktemp -d)
  105. # trap to delete temporary directory on exit
  106. trap "rm -rf $TMPDIR" EXIT
  107. # create temporary authorized_user_ids file
  108. TMP_AUTHORIZED_USER_IDS="${TMPDIR}/authorized_user_ids"
  109. touch "$TMP_AUTHORIZED_USER_IDS"
  110. # create temporary authorized_keys file
  111. AUTHORIZED_KEYS="${TMPDIR}/authorized_keys"
  112. touch "$AUTHORIZED_KEYS"
  113. # set restrictive permissions on the temporary files
  114. # FIXME: is there a better way to do this?
  115. chmod 0700 "$TMPDIR"
  116. chmod 0600 "$AUTHORIZED_KEYS"
  117. chmod 0600 "$TMP_AUTHORIZED_USER_IDS"
  118. chown -R "$MONKEYSPHERE_USER" "$TMPDIR"
  119. # if the authorized_user_ids file exists...
  120. if [ -s "$authorizedUserIDs" ] ; then
  121. # copy user authorized_user_ids file to temporary
  122. # location
  123. cat "$authorizedUserIDs" > "$TMP_AUTHORIZED_USER_IDS"
  124. # export needed variables
  125. export AUTHORIZED_KEYS
  126. export TMP_AUTHORIZED_USER_IDS
  127. # process authorized_user_ids file, as monkeysphere
  128. # user
  129. su_monkeysphere_user \
  130. ". ${SHARE}/common; process_authorized_user_ids $TMP_AUTHORIZED_USER_IDS"
  131. ERR="$?"
  132. fi
  133. # add user-controlled authorized_keys file path if specified
  134. if [ "$rawAuthorizedKeys" != '-' -a -s "$rawAuthorizedKeys" ] ; then
  135. log -n "adding raw authorized_keys file... "
  136. cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS"
  137. loge "done."
  138. fi
  139. # openssh appears to check the contents of the
  140. # authorized_keys file as the user in question, so the
  141. # file must be readable by that user at least.
  142. # FIXME: is there a better way to do this?
  143. chown root "$AUTHORIZED_KEYS"
  144. chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS"
  145. chmod g+r "$AUTHORIZED_KEYS"
  146. # if the resulting authorized_keys file is not empty, move
  147. # it into place
  148. mv -f "$AUTHORIZED_KEYS" "${VARLIB}/authorized_keys/${uname}"
  149. # destroy temporary directory
  150. rm -rf "$TMPDIR"
  151. done
  152. }
  153. # generate server gpg key
  154. gen_key() {
  155. local hostName
  156. local userID
  157. local keyParameters
  158. local fingerprint
  159. hostName=${1:-$(hostname --fqdn)}
  160. SERVICE=${SERVICE:-"ssh"}
  161. userID="${SERVICE}://${hostName}"
  162. if gpg_host --list-key ="$userID" > /dev/null 2>&1 ; then
  163. failure "Key for '$userID' already exists"
  164. fi
  165. # set key defaults
  166. KEY_TYPE=${KEY_TYPE:-"RSA"}
  167. KEY_LENGTH=${KEY_LENGTH:-"2048"}
  168. KEY_USAGE=${KEY_USAGE:-"auth"}
  169. KEY_EXPIRE=${KEY_EXPIRE:-"0"}
  170. cat <<EOF
  171. Please specify how long the key should be valid.
  172. 0 = key does not expire
  173. <n> = key expires in n days
  174. <n>w = key expires in n weeks
  175. <n>m = key expires in n months
  176. <n>y = key expires in n years
  177. EOF
  178. read -p "Key is valid for? ($KEY_EXPIRE) " KEY_EXPIRE; KEY_EXPIRE=${KEY_EXPIRE:-"0"}
  179. # set key parameters
  180. keyParameters=$(cat <<EOF
  181. Key-Type: $KEY_TYPE
  182. Key-Length: $KEY_LENGTH
  183. Key-Usage: $KEY_USAGE
  184. Name-Real: $userID
  185. Expire-Date: $KEY_EXPIRE
  186. EOF
  187. )
  188. # add the revoker field if specified
  189. # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key. why?
  190. # FIXME: why is this marked "sensitive"? how will this signature ever
  191. # be transmitted to the expected revoker?
  192. if [ "$REVOKER" ] ; then
  193. keyParameters="${keyParameters}"$(cat <<EOF
  194. Revoker: 1:$REVOKER sensitive
  195. EOF
  196. )
  197. fi
  198. echo "The following key parameters will be used for the host private key:"
  199. echo "$keyParameters"
  200. read -p "Generate key? (Y/n) " OK; OK=${OK:=Y}
  201. if [ ${OK/y/Y} != 'Y' ] ; then
  202. failure "aborting."
  203. fi
  204. # add commit command
  205. keyParameters="${keyParameters}"$(cat <<EOF
  206. %commit
  207. %echo done
  208. EOF
  209. )
  210. log "generating server key..."
  211. echo "$keyParameters" | gpg_host --batch --gen-key
  212. # output the server fingerprint
  213. fingerprint_server_key "=${userID}"
  214. # find the key fingerprint of the server primary key
  215. fingerprint=$(gpg_host --list-key --with-colons --with-fingerprint "=${userID}" | \
  216. grep '^fpr:' | head -1 | cut -d: -f10)
  217. # translate the private key to ssh format, and export to a file
  218. # for sshs usage.
  219. # NOTE: assumes that the primary key is the proper key to use
  220. (umask 077 && \
  221. gpg_host --export-secret-key "$fingerprint" | \
  222. openpgp2ssh "$fingerprint" > "${VARLIB}/ssh_host_rsa_key")
  223. log "Private SSH host key output to file: ${VARLIB}/ssh_host_rsa_key"
  224. }
  225. # gpg output key fingerprint
  226. fingerprint_server_key() {
  227. gpg_host --fingerprint --list-secret-keys
  228. }
  229. # publish server key to keyserver
  230. publish_server_key() {
  231. read -p "really publish key to $KEYSERVER? (y/N) " OK; OK=${OK:=N}
  232. if [ ${OK/y/Y} != 'Y' ] ; then
  233. failure "aborting."
  234. fi
  235. # publish host key
  236. # FIXME: need to figure out better way to identify host key
  237. # dummy command so as not to publish fakes keys during testing
  238. # eventually:
  239. #gpg_authentication "--keyring $GNUPGHOME_HOST/pubring.gpg --keyserver $KEYSERVER --send-keys $(hostname -f)"
  240. failure "NOT PUBLISHED (to avoid permanent publication errors during monkeysphere development)."
  241. }
  242. # retrieve key from web of trust, import it into the host keyring, and
  243. # ltsign the key in the host keyring so that it may certify other keys
  244. add_certifier() {
  245. local keyID
  246. local fingerprint
  247. local ltsignCommand
  248. keyID="$1"
  249. export keyID
  250. # export host ownertrust to authentication keyring
  251. gpg_host --export-ownertrust | gpg_authentication "--import-ownertrust"
  252. # get the key from the key server
  253. gpg_authentication "--keyserver $KEYSERVER --recv-key $keyID"
  254. # get the full fingerprint of a key ID
  255. fingerprint=$(gpg_authentication "--list-key --with-colons --with-fingerprint $keyID" | \
  256. grep '^fpr:' | grep "$keyID" | cut -d: -f10)
  257. if [ -z "$fingerprint" ] ; then
  258. failure "Could not find key '$keyID'."
  259. fi
  260. echo "key found:"
  261. gpg_authentication "--fingerprint $fingerprint"
  262. read -p "Are you sure you want to add this key as a certifier of users on this system? (y/N) " OK; OK=${OK:-N}
  263. if [ "${OK/y/Y}" != 'Y' ] ; then
  264. failure "aborting."
  265. fi
  266. # export the key to the host keyring
  267. gpg_authentication "--export $keyID" | gpg_host --import
  268. # default values for trust depth and domain
  269. DEPTH=${DEPTH:-1}
  270. DOMAIN=${DOMAIN:-}
  271. # ltsign command
  272. # NOTE: *all* user IDs will be ltsigned
  273. ltsignCommand=$(cat <<EOF
  274. ltsign
  275. y
  276. 2
  277. $DEPTH
  278. $DOMAIN
  279. y
  280. save
  281. EOF
  282. )
  283. # ltsign the key
  284. echo "$ltsignCommand" | gpg_host --quiet --command-fd 0 --edit-key "$fingerprint"
  285. # update the trustdb for the authentication keyring
  286. gpg_authentication "--check-trustdb"
  287. }
  288. # delete a certifiers key from the host keyring
  289. remove_certifier() {
  290. local keyID
  291. local fingerprint
  292. keyID="$1"
  293. # delete the requested key (with prompting)
  294. gpg_host --delete-key "$keyID"
  295. # update the trustdb for the authentication keyring
  296. gpg_authentication "--check-trustdb"
  297. }
  298. # list the host certifiers
  299. list_certifiers() {
  300. gpg_host --list-keys
  301. }
  302. ########################################################################
  303. # MAIN
  304. ########################################################################
  305. COMMAND="$1"
  306. [ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
  307. shift
  308. # load configuration file
  309. MS_CONF=${MS_CONF:-"${ETC}/monkeysphere-server.conf"}
  310. [ -e "$MS_CONF" ] && . "$MS_CONF"
  311. # set empty config variable with defaults
  312. MONKEYSPHERE_USER=${MONKEYSPHERE_USER:-"monkeysphere"}
  313. KEYSERVER=${KEYSERVER:-"subkeys.pgp.net"}
  314. CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"}
  315. AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"%h/.config/monkeysphere/authorized_user_ids"}
  316. RAW_AUTHORIZED_KEYS=${RAW_AUTHORIZED_KEYS:-"%h/.ssh/authorized_keys"}
  317. # other variables
  318. REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
  319. GNUPGHOME_HOST=${GNUPGHOME_HOST:-"${VARLIB}/gnupg-host"}
  320. GNUPGHOME_AUTHENTICATION=${GNUPGHOME_AUTHENTICATION:-"${VARLIB}/gnupg-authentication"}
  321. # export variables
  322. export DATE
  323. export MODE
  324. export MONKEYSPHERE_USER
  325. export KEYSERVER
  326. export CHECK_KEYSERVER
  327. export REQUIRED_USER_KEY_CAPABILITY
  328. export GNUPGHOME_HOST
  329. export GNUPGHOME_AUTHENTICATION
  330. export GNUPGHOME
  331. case $COMMAND in
  332. 'update-users'|'update-user'|'u')
  333. update_users "$@"
  334. ;;
  335. 'gen-key'|'g')
  336. gen_key "$@"
  337. ;;
  338. 'show-fingerprint'|'f')
  339. fingerprint_server_key
  340. ;;
  341. 'publish-key'|'p')
  342. publish_server_key
  343. ;;
  344. 'add-certifier'|'a')
  345. if [ -z "$1" ] ; then
  346. failure "You must specify a key ID."
  347. fi
  348. add_certifier "$1"
  349. ;;
  350. 'remove-certifier'|'r')
  351. if [ -z "$1" ] ; then
  352. failure "You must specify a key ID."
  353. fi
  354. remove_certifier "$1"
  355. ;;
  356. 'list-certifiers'|'l')
  357. list_certifiers "$@"
  358. ;;
  359. 'help'|'h'|'?')
  360. usage
  361. ;;
  362. *)
  363. failure "Unknown command: '$COMMAND'
  364. Type '$PGRM help' for usage."
  365. ;;
  366. esac
  367. exit "$ERR"