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