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