summaryrefslogtreecommitdiff
path: root/src/monkeysphere-server
blob: 3c4eed406774db0e9bcdb7795c91e43989520cf4 (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. # Daniel Kahn Gillmor <dkg@fifthhorseman.net>
  7. #
  8. # They are Copyright 2008, and are all released under the GPL, version 3
  9. # or later.
  10. ########################################################################
  11. PGRM=$(basename $0)
  12. SHARE=${MONKEYSPHERE_SHARE:="/usr/share/monkeysphere"}
  13. export SHARE
  14. . "${SHARE}/common" || exit 1
  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. unset GREP_OPTIONS
  21. # default return code
  22. RETURN=0
  23. ########################################################################
  24. # FUNCTIONS
  25. ########################################################################
  26. usage() {
  27. cat <<EOF
  28. usage: $PGRM <subcommand> [options] [args]
  29. MonkeySphere server admin tool.
  30. subcommands:
  31. update-users (u) [USER]... update user authorized_keys files
  32. gen-key (g) [NAME[:PORT]] generate gpg key for the server
  33. --length (-l) BITS key length in bits (2048)
  34. --expire (-e) EXPIRE date to expire
  35. --revoker (-r) FINGERPRINT add a revoker
  36. extend-key (e) EXPIRE extend expiration to EXPIRE
  37. add-hostname (n+) NAME[:PORT] add hostname user ID to server key
  38. revoke-hostname (n-) NAME[:PORT] revoke hostname user ID
  39. show-key (s) output all server host key information
  40. publish-key (p) publish server host key to keyserver
  41. diagnostics (d) report on server monkeysphere status
  42. add-id-certifier (c+) KEYID import and tsign a certification key
  43. --domain (-n) DOMAIN limit ID certifications to DOMAIN
  44. --trust (-t) TRUST trust level of certifier (full)
  45. --depth (-d) DEPTH trust depth for certifier (1)
  46. remove-id-certifier (c-) KEYID remove a certification key
  47. list-id-certifiers (c) list certification keys
  48. gpg-authentication-cmd CMD gnupg-authentication command
  49. help (h,?) this help
  50. EOF
  51. }
  52. su_monkeysphere_user() {
  53. su --preserve-environment "$MONKEYSPHERE_USER" -- -c "$@"
  54. }
  55. # function to interact with the host gnupg keyring
  56. gpg_host() {
  57. local returnCode
  58. GNUPGHOME="$GNUPGHOME_HOST"
  59. export GNUPGHOME
  60. # NOTE: we supress this warning because we need the monkeysphere
  61. # user to be able to read the host pubring. we realize this might
  62. # be problematic, but it's the simplest solution, without too much
  63. # loss of security.
  64. gpg --no-permission-warning "$@"
  65. returnCode="$?"
  66. # always reset the permissions on the host pubring so that the
  67. # monkeysphere user can read the trust signatures
  68. chgrp "$MONKEYSPHERE_USER" "${GNUPGHOME_HOST}/pubring.gpg"
  69. chmod g+r "${GNUPGHOME_HOST}/pubring.gpg"
  70. return "$returnCode"
  71. }
  72. # function to interact with the authentication gnupg keyring
  73. # FIXME: this function requires basically accepts only a single
  74. # argument because of problems with quote expansion. this needs to be
  75. # fixed/improved.
  76. gpg_authentication() {
  77. GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
  78. export GNUPGHOME
  79. su_monkeysphere_user "gpg $@"
  80. }
  81. # output just key fingerprint
  82. fingerprint_server_key() {
  83. gpg_host --list-secret-keys --fingerprint \
  84. --with-colons --fixed-list-mode 2> /dev/null | \
  85. grep '^fpr:' | head -1 | cut -d: -f10
  86. }
  87. # output key information
  88. show_server_key() {
  89. local fingerprint
  90. local tmpkey
  91. fingerprint=$(fingerprint_server_key)
  92. gpg_authentication "--fingerprint --list-key --list-options show-unusable-uids $fingerprint"
  93. # dumping to a file named ' ' so that the ssh-keygen output
  94. # doesn't claim any potentially bogus hostname(s):
  95. tmpkey=$(mktemp -d)
  96. gpg_authentication "--export $fingerprint" | openpgp2ssh "$fingerprint" 2>/dev/null > "$tmpkey/ "
  97. echo -n "ssh fingerprint: "
  98. (cd "$tmpkey" && ssh-keygen -l -f ' ' | awk '{ print $2 }')
  99. rm -rf "$tmpkey"
  100. echo -n "OpenPGP fingerprint: "
  101. echo "$fingerprint"
  102. }
  103. # update authorized_keys for users
  104. update_users() {
  105. if [ "$1" ] ; then
  106. # get users from command line
  107. unames="$@"
  108. else
  109. # or just look at all users if none specified
  110. unames=$(getent passwd | cut -d: -f1)
  111. fi
  112. # set mode
  113. MODE="authorized_keys"
  114. # set gnupg home
  115. GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
  116. # check to see if the gpg trust database has been initialized
  117. if [ ! -s "${GNUPGHOME}/trustdb.gpg" ] ; then
  118. failure "GNUPG trust database uninitialized. Please see MONKEYSPHERE-SERVER(8)."
  119. fi
  120. # make sure the authorized_keys directory exists
  121. mkdir -p "${VARLIB}/authorized_keys"
  122. # loop over users
  123. for uname in $unames ; do
  124. # check all specified users exist
  125. if ! getent passwd "$uname" >/dev/null ; then
  126. log "----- unknown user '$uname' -----"
  127. continue
  128. fi
  129. # set authorized_user_ids and raw authorized_keys variables,
  130. # translating ssh-style path variables
  131. authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS")
  132. rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS")
  133. # if neither is found, skip user
  134. if [ ! -s "$authorizedUserIDs" ] ; then
  135. if [ "$rawAuthorizedKeys" = '-' -o ! -s "$rawAuthorizedKeys" ] ; then
  136. continue
  137. fi
  138. fi
  139. log "----- user: $uname -----"
  140. # exit if the authorized_user_ids file is empty
  141. if ! check_key_file_permissions "$uname" "$AUTHORIZED_USER_IDS" ; then
  142. log "Improper permissions on authorized_user_ids file path."
  143. continue
  144. fi
  145. # check permissions on the authorized_keys file path
  146. if ! check_key_file_permissions "$uname" "$RAW_AUTHORIZED_KEYS" ; then
  147. log "Improper permissions on authorized_keys file path path."
  148. continue
  149. fi
  150. # make temporary directory
  151. TMPDIR=$(mktemp -d)
  152. # trap to delete temporary directory on exit
  153. trap "rm -rf $TMPDIR" EXIT
  154. # create temporary authorized_user_ids file
  155. TMP_AUTHORIZED_USER_IDS="${TMPDIR}/authorized_user_ids"
  156. touch "$TMP_AUTHORIZED_USER_IDS"
  157. # create temporary authorized_keys file
  158. AUTHORIZED_KEYS="${TMPDIR}/authorized_keys"
  159. touch "$AUTHORIZED_KEYS"
  160. # set restrictive permissions on the temporary files
  161. # FIXME: is there a better way to do this?
  162. chmod 0700 "$TMPDIR"
  163. chmod 0600 "$AUTHORIZED_KEYS"
  164. chmod 0600 "$TMP_AUTHORIZED_USER_IDS"
  165. chown -R "$MONKEYSPHERE_USER" "$TMPDIR"
  166. # if the authorized_user_ids file exists...
  167. if [ -s "$authorizedUserIDs" ] ; then
  168. # copy user authorized_user_ids file to temporary
  169. # location
  170. cat "$authorizedUserIDs" > "$TMP_AUTHORIZED_USER_IDS"
  171. # export needed variables
  172. export AUTHORIZED_KEYS
  173. export TMP_AUTHORIZED_USER_IDS
  174. # process authorized_user_ids file, as monkeysphere
  175. # user
  176. su_monkeysphere_user \
  177. ". ${SHARE}/common; process_authorized_user_ids $TMP_AUTHORIZED_USER_IDS"
  178. RETURN="$?"
  179. fi
  180. # add user-controlled authorized_keys file path if specified
  181. if [ "$rawAuthorizedKeys" != '-' -a -s "$rawAuthorizedKeys" ] ; then
  182. log -n "adding raw authorized_keys file... "
  183. cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS"
  184. loge "done."
  185. fi
  186. # openssh appears to check the contents of the
  187. # authorized_keys file as the user in question, so the
  188. # file must be readable by that user at least.
  189. # FIXME: is there a better way to do this?
  190. chown root "$AUTHORIZED_KEYS"
  191. chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS"
  192. chmod g+r "$AUTHORIZED_KEYS"
  193. # move the resulting authorized_keys file into place
  194. mv -f "$AUTHORIZED_KEYS" "${VARLIB}/authorized_keys/${uname}"
  195. # destroy temporary directory
  196. rm -rf "$TMPDIR"
  197. done
  198. }
  199. # generate server gpg key
  200. gen_key() {
  201. local keyType
  202. local keyLength
  203. local keyUsage
  204. local keyExpire
  205. local revoker
  206. local hostName
  207. local userID
  208. local keyParameters
  209. local fingerprint
  210. # set default key parameter values
  211. keyType="RSA"
  212. keyLength="2048"
  213. keyUsage="auth"
  214. keyExpire=
  215. revoker=
  216. # get options
  217. TEMP=$(getopt -o e:l:r -l expire:,length:,revoker: -n "$PGRM" -- "$@")
  218. if [ $? != 0 ] ; then
  219. exit 1
  220. fi
  221. # Note the quotes around `$TEMP': they are essential!
  222. eval set -- "$TEMP"
  223. while true ; do
  224. case "$1" in
  225. -l|--length)
  226. keyLength="$2"
  227. shift 2
  228. ;;
  229. -e|--expire)
  230. keyExpire="$2"
  231. shift 2
  232. ;;
  233. -r|--revoker)
  234. revoker="$2"
  235. shift 2
  236. ;;
  237. --)
  238. shift
  239. ;;
  240. *)
  241. break
  242. ;;
  243. esac
  244. done
  245. hostName=${1:-$(hostname --fqdn)}
  246. userID="ssh://${hostName}"
  247. # check for presense of key with user ID
  248. if gpg_host --list-key ="$userID" > /dev/null 2>&1 ; then
  249. failure "Key for '$userID' already exists"
  250. fi
  251. # prompt about key expiration if not specified
  252. if [ -z "$keyExpire" ] ; then
  253. keyExpire=$(get_gpg_expiration)
  254. fi
  255. if ! test_gpg_expire "$keyExpire" ; then
  256. failure "invalid key expiration value '$keyExpire'."
  257. fi
  258. # set key parameters
  259. keyParameters=$(cat <<EOF
  260. Key-Type: $keyType
  261. Key-Length: $keyLength
  262. Key-Usage: $keyUsage
  263. Name-Real: $userID
  264. Expire-Date: $keyExpire
  265. EOF
  266. )
  267. # add the revoker field if specified
  268. # FIXME: the "1:" below assumes that $REVOKER's key is an RSA key.
  269. # FIXME: key is marked "sensitive"? is this appropriate?
  270. if [ "$revoker" ] ; then
  271. keyParameters="${keyParameters}"$(cat <<EOF
  272. Revoker: 1:$revoker sensitive
  273. EOF
  274. )
  275. fi
  276. echo "The following key parameters will be used for the host private key:"
  277. echo "$keyParameters"
  278. read -p "Generate key? (Y/n) " OK; OK=${OK:=Y}
  279. if [ ${OK/y/Y} != 'Y' ] ; then
  280. failure "aborting."
  281. fi
  282. # add commit command
  283. keyParameters="${keyParameters}"$(cat <<EOF
  284. %commit
  285. %echo done
  286. EOF
  287. )
  288. log "generating server key..."
  289. echo "$keyParameters" | gpg_host --batch --gen-key
  290. # output the server fingerprint
  291. fingerprint_server_key "=${userID}"
  292. # find the key fingerprint of the newly generated key
  293. fingerprint=$(fingerprint_server_key)
  294. # export host ownertrust to authentication keyring
  295. log "setting ultimate owner trust for server key..."
  296. echo "${fingerprint}:6:" | gpg_authentication "--import-ownertrust"
  297. # translate the private key to ssh format, and export to a file
  298. # for sshs usage.
  299. # NOTE: assumes that the primary key is the proper key to use
  300. (umask 077 && \
  301. gpg_host --export-secret-key "$fingerprint" | \
  302. openpgp2ssh "$fingerprint" > "${VARLIB}/ssh_host_rsa_key")
  303. log "Private SSH host key output to file: ${VARLIB}/ssh_host_rsa_key"
  304. }
  305. # extend the lifetime of a host key:
  306. extend_key() {
  307. local fpr=$(fingerprint_server_key)
  308. local extendTo="$1"
  309. if [ -z "$fpr" ] ; then
  310. failure "You don't appear to have a MonkeySphere host key on this server. Try 'monkeysphere-server gen-key' first."
  311. fi
  312. if [ -z "$extendTo" ]; then
  313. extendTo=$(get_gpg_expiration)
  314. fi
  315. if ! test_gpg_expire "$extendTo" ; then
  316. failure "invalid expiration value '$extendTo'."
  317. fi
  318. gpg_host --quiet --command-fd 0 --edit-key "$fpr" <<EOF
  319. expire
  320. $extendTo
  321. save
  322. EOF
  323. echo
  324. echo "NOTE: Host key expiration date adjusted, but not yet published."
  325. echo "Run '$PGRM publish-key' to publish the new expiration date."
  326. }
  327. # add hostname user ID to server key
  328. add_hostname() {
  329. local userID
  330. local fingerprint
  331. local tmpuidMatch
  332. local line
  333. local adduidCommand
  334. if [ -z "$1" ] ; then
  335. failure "You must specify a hostname to add."
  336. fi
  337. userID="ssh://${1}"
  338. fingerprint=$(fingerprint_server_key)
  339. # match to only ultimately trusted user IDs
  340. tmpuidMatch="u:$(echo $userID | gpg_escape)"
  341. # find the index of the requsted user ID
  342. # NOTE: this is based on circumstantial evidence that the order of
  343. # this output is the appropriate index
  344. if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \
  345. | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then
  346. failure "Host userID '$userID' already exists."
  347. fi
  348. echo "The following user ID will be added to the host key:"
  349. echo " $userID"
  350. read -p "Are you sure you would like to add this user ID? (y/N) " OK; OK=${OK:=N}
  351. if [ ${OK/y/Y} != 'Y' ] ; then
  352. failure "User ID not added."
  353. fi
  354. # edit-key script command to add user ID
  355. adduidCommand=$(cat <<EOF
  356. adduid
  357. $userID
  358. save
  359. EOF
  360. )
  361. # execute edit-key script
  362. if echo "$adduidCommand" | \
  363. gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then
  364. # update the trustdb for the authentication keyring
  365. gpg_authentication "--check-trustdb"
  366. show_server_key
  367. echo
  368. echo "NOTE: User ID added to key, but key not published."
  369. echo "Run '$PGRM publish-key' to publish the new user ID."
  370. else
  371. failure "Problem adding user ID."
  372. fi
  373. }
  374. # revoke hostname user ID to server key
  375. revoke_hostname() {
  376. local userID
  377. local fingerprint
  378. local tmpuidMatch
  379. local line
  380. local uidIndex
  381. local message
  382. local revuidCommand
  383. if [ -z "$1" ] ; then
  384. failure "You must specify a hostname to revoke."
  385. fi
  386. echo "WARNING: There is a known bug in this function."
  387. echo "This function has been known to occasionally revoke the wrong user ID."
  388. echo "Please see the following bug report for more information:"
  389. echo "http://monkeysphere.info/bugs/revoke-hostname-revoking-wrong-userid/"
  390. read -p "Are you sure you would like to proceed? (y/N) " OK; OK=${OK:=N}
  391. if [ ${OK/y/Y} != 'Y' ] ; then
  392. failure "aborting."
  393. fi
  394. userID="ssh://${1}"
  395. fingerprint=$(fingerprint_server_key)
  396. # match to only ultimately trusted user IDs
  397. tmpuidMatch="u:$(echo $userID | gpg_escape)"
  398. # find the index of the requsted user ID
  399. # NOTE: this is based on circumstantial evidence that the order of
  400. # this output is the appropriate index
  401. if line=$(gpg_host --list-keys --with-colons --fixed-list-mode "0x${fingerprint}!" \
  402. | egrep '^(uid|uat):' | cut -f2,10 -d: | grep -n -x -F "$tmpuidMatch") ; then
  403. uidIndex=${line%%:*}
  404. else
  405. failure "No non-revoked user ID '$userID' is found."
  406. fi
  407. echo "The following host key user ID will be revoked:"
  408. echo " $userID"
  409. read -p "Are you sure you would like to revoke this user ID? (y/N) " OK; OK=${OK:=N}
  410. if [ ${OK/y/Y} != 'Y' ] ; then
  411. failure "User ID not revoked."
  412. fi
  413. message="Hostname removed by monkeysphere-server $DATE"
  414. # edit-key script command to revoke user ID
  415. revuidCommand=$(cat <<EOF
  416. $uidIndex
  417. revuid
  418. y
  419. 4
  420. $message
  421. y
  422. save
  423. EOF
  424. )
  425. # execute edit-key script
  426. if echo "$revuidCommand" | \
  427. gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then
  428. # update the trustdb for the authentication keyring
  429. gpg_authentication "--check-trustdb"
  430. show_server_key
  431. echo
  432. echo "NOTE: User ID revoked, but revocation not published."
  433. echo "Run '$PGRM publish-key' to publish the revocation."
  434. else
  435. failure "Problem revoking user ID."
  436. fi
  437. }
  438. # publish server key to keyserver
  439. publish_server_key() {
  440. read -p "Really publish host key to $KEYSERVER? (y/N) " OK; OK=${OK:=N}
  441. if [ ${OK/y/Y} != 'Y' ] ; then
  442. failure "key not published."
  443. fi
  444. # find the key fingerprint
  445. fingerprint=$(fingerprint_server_key)
  446. # publish host key
  447. gpg_authentication "--keyserver $KEYSERVER --send-keys '0x${fingerprint}!'"
  448. }
  449. diagnostics() {
  450. # * check on the status and validity of the key and public certificates
  451. local seckey
  452. local keysfound
  453. local curdate
  454. local warnwindow
  455. local warndate
  456. local create
  457. local expire
  458. local uid
  459. local fingerprint
  460. local badhostkeys
  461. local sshd_config
  462. # FIXME: what's the correct, cross-platform answer?
  463. sshd_config=/etc/ssh/sshd_config
  464. seckey=$(gpg_host --list-secret-keys --fingerprint --with-colons --fixed-list-mode)
  465. keysfound=$(echo "$seckey" | grep -c ^sec:)
  466. curdate=$(date +%s)
  467. # warn when anything is 2 months away from expiration
  468. warnwindow='2 months'
  469. warndate=$(date +%s -d "$warnwindow")
  470. echo "Checking host GPG key..."
  471. if (( "$keysfound" < 1 )); then
  472. echo "! No host key found."
  473. echo " - Recommendation: run 'monkeysphere-server gen-key'"
  474. elif (( "$keysfound" > 1 )); then
  475. echo "! More than one host key found?"
  476. # FIXME: recommend a way to resolve this
  477. else
  478. create=$(echo "$seckey" | grep ^sec: | cut -f6 -d:)
  479. expire=$(echo "$seckey" | grep ^sec: | cut -f7 -d:)
  480. fingerprint=$(echo "$seckey" | grep ^fpr: | head -n1 | cut -f10 -d:)
  481. # check for key expiration:
  482. if [ "$expire" ]; then
  483. if (( "$expire" < "$curdate" )); then
  484. echo "! Host key is expired."
  485. echo " - Recommendation: extend lifetime of key with 'monkeysphere-server extend-key'"
  486. elif (( "$expire" < "$warndate" )); then
  487. echo "! Host key expires in less than $warnwindow:" $(date -d "$(( $expire - $curdate )) seconds" +%F)
  488. echo " - Recommendation: extend lifetime of key with 'monkeysphere-server extend-key'"
  489. fi
  490. fi
  491. # and weirdnesses:
  492. if [ "$create" ] && (( "$create" > "$curdate" )); then
  493. echo "! Host key was created in the future(?!). Is your clock correct?"
  494. echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?"
  495. fi
  496. # check for UserID expiration:
  497. echo "$seckey" | grep ^uid: | cut -d: -f6,7,10 | \
  498. while IFS=: read create expire uid ; do
  499. # FIXME: should we be doing any checking on the form
  500. # of the User ID? Should we be unmangling it somehow?
  501. if [ "$create" ] && (( "$create" > "$curdate" )); then
  502. echo "! User ID '$uid' was created in the future(?!). Is your clock correct?"
  503. echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?"
  504. fi
  505. if [ "$expire" ] ; then
  506. if (( "$expire" < "$curdate" )); then
  507. echo "! User ID '$uid' is expired."
  508. # FIXME: recommend a way to resolve this
  509. elif (( "$expire" < "$warndate" )); then
  510. echo "! User ID '$uid' expires in less than $warnwindow:" $(date -d "$(( $expire - $curdate )) seconds" +%F)
  511. # FIXME: recommend a way to resolve this
  512. fi
  513. fi
  514. done
  515. # FIXME: verify that the host key is properly published to the
  516. # keyservers (do this with the non-privileged user)
  517. # FIXME: check that there are valid, non-expired certifying signatures
  518. # attached to the host key after fetching from the public keyserver
  519. # (do this with the non-privileged user as well)
  520. # FIXME: propose adding a revoker to the host key if none exist (do we
  521. # have a way to do that after key generation?)
  522. # Ensure that the ssh_host_rsa_key file is present and non-empty:
  523. echo
  524. echo "Checking host SSH key..."
  525. if [ ! -s "${VARLIB}/ssh_host_rsa_key" ] ; then
  526. echo "! The host key as prepared for SSH (${VARLIB}/ssh_host_rsa_key) is missing or empty."
  527. else
  528. if [ $(stat -c '%a' "${VARLIB}/ssh_host_rsa_key") != 600 ] ; then
  529. echo "! Permissions seem wrong for ${VARLIB}/ssh_host_rsa_key -- should be 0600."
  530. fi
  531. # propose changes needed for sshd_config (if any)
  532. if ! grep -q "^HostKey[[:space:]]\+${VARLIB}/ssh_host_rsa_key$" "$sshd_config"; then
  533. echo "! $sshd_config does not point to the monkeysphere host key (${VARLIB}/ssh_host_rsa_key)."
  534. echo " - Recommendation: add a line to $sshd_config: 'HostKey ${VARLIB}/ssh_host_rsa_key'"
  535. fi
  536. if badhostkeys=$(grep -i '^HostKey' "$sshd_config" | grep -q -v "^HostKey[[:space:]]\+${VARLIB}/ssh_host_rsa_key$") ; then
  537. echo "! $sshd_config refers to some non-monkeysphere host keys:"
  538. echo "$badhostkeys"
  539. echo " - Recommendation: remove the above HostKey lines from $sshd_config"
  540. fi
  541. fi
  542. fi
  543. # FIXME: look at the ownership/privileges of the various keyrings,
  544. # directories housing them, etc (what should those values be? can
  545. # we make them as minimal as possible?)
  546. # FIXME: look to see that the ownertrust rules are set properly on the
  547. # authentication keyring
  548. # FIXME: make sure that at least one identity certifier exists
  549. echo
  550. echo "Checking for MonkeySphere-enabled public-key authentication for users ..."
  551. # Ensure that User ID authentication is enabled:
  552. if ! grep -q "^AuthorizedKeysFile[[:space:]]\+${VARLIB}/authorized_keys/%u$" "$sshd_config"; then
  553. echo "! $sshd_config does not point to monkeysphere authorized keys."
  554. echo " - Recommendation: add a line to $sshd_config: 'AuthorizedKeysFile ${VARLIB}/authorized_keys/%u'"
  555. fi
  556. if badauthorizedkeys=$(grep -i '^AuthorizedKeysFile' "$sshd_config" | grep -q -v "^AuthorizedKeysFile[[:space:]]\+${VARLIB}/authorized_keys/%u$") ; then
  557. echo "! $sshd_config refers to non-monkeysphere authorized_keys files:"
  558. echo "$badauthorizedkeys"
  559. echo " - Recommendation: remove the above AuthorizedKeysFile lines from $sshd_config"
  560. fi
  561. }
  562. # retrieve key from web of trust, import it into the host keyring, and
  563. # ltsign the key in the host keyring so that it may certify other keys
  564. add_certifier() {
  565. local domain
  566. local trust
  567. local depth
  568. local keyID
  569. local fingerprint
  570. local ltsignCommand
  571. local trustval
  572. # set default values for trust depth and domain
  573. domain=
  574. trust=full
  575. depth=1
  576. # get options
  577. TEMP=$(getopt -o n:t:d: -l domain:,trust:,depth: -n "$PGRM" -- "$@")
  578. if [ $? != 0 ] ; then
  579. exit 1
  580. fi
  581. # Note the quotes around `$TEMP': they are essential!
  582. eval set -- "$TEMP"
  583. while true ; do
  584. case "$1" in
  585. -n|--domain)
  586. domain="$2"
  587. shift 2
  588. ;;
  589. -t|--trust)
  590. trust="$2"
  591. shift 2
  592. ;;
  593. -d|--depth)
  594. depth="$2"
  595. shift 2
  596. ;;
  597. --)
  598. shift
  599. ;;
  600. *)
  601. break
  602. ;;
  603. esac
  604. done
  605. keyID="$1"
  606. if [ -z "$keyID" ] ; then
  607. failure "You must specify the key ID of a key to add."
  608. fi
  609. export keyID
  610. # get the key from the key server
  611. gpg_authentication "--keyserver $KEYSERVER --recv-key '0x${keyID}!'"
  612. # get the full fingerprint of a key ID
  613. fingerprint=$(gpg_authentication "--list-key --with-colons --with-fingerprint 0x${keyID}!" | \
  614. grep '^fpr:' | grep "$keyID" | cut -d: -f10)
  615. if [ -z "$fingerprint" ] ; then
  616. failure "Key '$keyID' not found."
  617. fi
  618. echo
  619. echo "key found:"
  620. gpg_authentication "--fingerprint 0x${fingerprint}!"
  621. echo "Are you sure you want to add the above key as a"
  622. read -p "certifier of users on this system? (y/N) " OK; OK=${OK:-N}
  623. if [ "${OK/y/Y}" != 'Y' ] ; then
  624. failure "Identity certifier not added."
  625. fi
  626. # export the key to the host keyring
  627. gpg_authentication "--export 0x${fingerprint}!" | gpg_host --import
  628. if [ "$trust" == marginal ]; then
  629. trustval=1
  630. elif [ "$trust" == full ]; then
  631. trustval=2
  632. else
  633. failure "Trust value requested ('$trust') was unclear (only 'marginal' or 'full' are supported)."
  634. fi
  635. # ltsign command
  636. # NOTE: *all* user IDs will be ltsigned
  637. ltsignCommand=$(cat <<EOF
  638. ltsign
  639. y
  640. $trustval
  641. $depth
  642. $domain
  643. y
  644. save
  645. EOF
  646. )
  647. # ltsign the key
  648. if echo "$ltsignCommand" | \
  649. gpg_host --quiet --command-fd 0 --edit-key "0x${fingerprint}!" ; then
  650. # update the trustdb for the authentication keyring
  651. gpg_authentication "--check-trustdb"
  652. echo
  653. echo "Identity certifier added."
  654. else
  655. failure "Problem adding identify certifier."
  656. fi
  657. }
  658. # delete a certifiers key from the host keyring
  659. remove_certifier() {
  660. local keyID
  661. local fingerprint
  662. keyID="$1"
  663. if [ -z "$keyID" ] ; then
  664. failure "You must specify the key ID of a key to remove."
  665. fi
  666. if gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-key 0x${keyID}!" ; then
  667. read -p "Really remove above listed identity certifier? (y/N) " OK; OK=${OK:-N}
  668. if [ "${OK/y/Y}" != 'Y' ] ; then
  669. failure "Identity certifier not removed."
  670. fi
  671. else
  672. failure
  673. fi
  674. # delete the requested key
  675. if gpg_authentication "--delete-key --batch --yes 0x${keyID}!" ; then
  676. # delete key from host keyring as well
  677. gpg_host --delete-key --batch --yes "0x${keyID}!"
  678. # update the trustdb for the authentication keyring
  679. gpg_authentication "--check-trustdb"
  680. echo
  681. echo "Identity certifier removed."
  682. else
  683. failure "Problem removing identity certifier."
  684. fi
  685. }
  686. # list the host certifiers
  687. list_certifiers() {
  688. local keys
  689. local key
  690. # find trusted keys in authentication keychain
  691. keys=$(gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-keys --with-colons --fingerprint" | \
  692. grep ^pub: | cut -d: -f2,5 | egrep '^(u|f):' | cut -d: -f2)
  693. # output keys
  694. for key in $keys ; do
  695. gpg_authentication "--no-options --list-options show-uid-validity --keyring ${GNUPGHOME_AUTHENTICATION}/pubring.gpg --list-key --fingerprint $key"
  696. done
  697. }
  698. # issue command to gpg-authentication keyring
  699. gpg_authentication_cmd() {
  700. gpg_authentication "$@"
  701. }
  702. ########################################################################
  703. # MAIN
  704. ########################################################################
  705. # unset variables that should be defined only in config file
  706. unset KEYSERVER
  707. unset AUTHORIZED_USER_IDS
  708. unset RAW_AUTHORIZED_KEYS
  709. unset MONKEYSPHERE_USER
  710. # load configuration file
  711. [ -e ${MONKEYSPHERE_SERVER_CONFIG:="${ETC}/monkeysphere-server.conf"} ] && . "$MONKEYSPHERE_SERVER_CONFIG"
  712. # set empty config variable with ones from the environment, or with
  713. # defaults
  714. KEYSERVER=${MONKEYSPHERE_KEYSERVER:=${KEYSERVER:="subkeys.pgp.net"}}
  715. AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:=${AUTHORIZED_USER_IDS:="%h/.config/monkeysphere/authorized_user_ids"}}
  716. RAW_AUTHORIZED_KEYS=${MONKEYSPHERE_RAW_AUTHORIZED_KEYS:=${RAW_AUTHORIZED_KEYS:="%h/.ssh/authorized_keys"}}
  717. MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=${MONKEYSPHERE_USER:="monkeysphere"}}
  718. # other variables
  719. CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:="true"}
  720. REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"}
  721. GNUPGHOME_HOST=${MONKEYSPHERE_GNUPGHOME_HOST:="${VARLIB}/gnupg-host"}
  722. GNUPGHOME_AUTHENTICATION=${MONKEYSPHERE_GNUPGHOME_AUTHENTICATION:="${VARLIB}/gnupg-authentication"}
  723. # export variables needed in su invocation
  724. export DATE
  725. export MODE
  726. export MONKEYSPHERE_USER
  727. export KEYSERVER
  728. export CHECK_KEYSERVER
  729. export REQUIRED_USER_KEY_CAPABILITY
  730. export GNUPGHOME_HOST
  731. export GNUPGHOME_AUTHENTICATION
  732. export GNUPGHOME
  733. # get subcommand
  734. COMMAND="$1"
  735. [ "$COMMAND" ] || failure "Type '$PGRM help' for usage."
  736. shift
  737. case $COMMAND in
  738. 'update-users'|'update-user'|'u')
  739. update_users "$@"
  740. ;;
  741. 'gen-key'|'g')
  742. gen_key "$@"
  743. ;;
  744. 'extend-key'|'e')
  745. extend_key "$@"
  746. ;;
  747. 'add-hostname'|'add-name'|'n+')
  748. add_hostname "$@"
  749. ;;
  750. 'revoke-hostname'|'revoke-name'|'n-')
  751. revoke_hostname "$@"
  752. ;;
  753. 'show-key'|'show'|'s')
  754. show_server_key
  755. ;;
  756. 'publish-key'|'publish'|'p')
  757. publish_server_key
  758. ;;
  759. 'diagnostics'|'d')
  760. diagnostics
  761. ;;
  762. 'add-identity-certifier'|'add-id-certifier'|'add-certifier'|'c+')
  763. add_certifier "$@"
  764. ;;
  765. 'remove-identity-certifier'|'remove-id-certifier'|'remove-certifier'|'c-')
  766. remove_certifier "$@"
  767. ;;
  768. 'list-identity-certifiers'|'list-id-certifiers'|'list-certifiers'|'list-certifier'|'c')
  769. list_certifiers "$@"
  770. ;;
  771. 'gpg-authentication-cmd')
  772. gpg_authentication_cmd "$@"
  773. ;;
  774. '--help'|'help'|'-h'|'h'|'?')
  775. usage
  776. ;;
  777. *)
  778. failure "Unknown command: '$COMMAND'
  779. Type '$PGRM help' for usage."
  780. ;;
  781. esac
  782. exit "$RETURN"