summaryrefslogtreecommitdiff
path: root/rhesus/rhesus
blob: 2e05dfda30d5a89050396b1fdfdcf4481db0e784 (plain)
  1. #!/bin/sh -e
  2. # rhesus: monkeysphere authorized_keys/known_hosts generating script
  3. #
  4. # Written by
  5. # Jameson Rollins <jrollins@fifthhorseman.net>
  6. #
  7. # Copyright 2008, released under the GPL, version 3 or later
  8. CMD=$(basename $0)
  9. ########################################################################
  10. # FUNCTIONS
  11. ########################################################################
  12. usage() {
  13. cat <<EOF
  14. usage: $CMD -k|--known_hosts
  15. $CMD -a|--authorized_keys
  16. EOF
  17. }
  18. failure() {
  19. echo "$1" >&2
  20. exit ${2:-'1'}
  21. }
  22. log() {
  23. echo -n "ms: "
  24. echo "$@"
  25. }
  26. # cut out all comments(#) and blank lines from standard input
  27. meat() {
  28. grep -v -e "^[[:space:]]*#" -e '^$'
  29. }
  30. # cut a specified line from standard input
  31. cutline() {
  32. head --line="$1" | tail -1
  33. }
  34. # retrieve all keys with given user id from keyserver
  35. # FIXME: need to figure out how to retrieve all matching keys
  36. # (not just first 5)
  37. gpg_fetch_keys() {
  38. local id="$1"
  39. echo 1,2,3,4,5 | \
  40. gpg --quiet --batch --command-fd 0 --with-colons \
  41. --keyserver "$KEYSERVER" \
  42. --search ="$id" >/dev/null 2>&1
  43. }
  44. # convert escaped characters from gpg output back into original
  45. # character
  46. # FIXME: undo all escape character translation in with-colons gpg output
  47. unescape() {
  48. echo "$1" | sed 's/\\x3a/:/'
  49. }
  50. # stand in until we get dkg's gpg2ssh program
  51. gpg2ssh_tmp() {
  52. local mode
  53. local keyID
  54. mode="$1"
  55. keyID="$2"
  56. userID="$3"
  57. if [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then
  58. gpgkey2ssh "$keyID" | sed -e "s/COMMENT/$userID/"
  59. elif [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then
  60. echo -n "$userID "; gpgkey2ssh "$keyID" | sed -e 's/ COMMENT//'
  61. fi
  62. }
  63. # userid and key policy checking
  64. # the following checks policy on the returned keys
  65. # - checks that full key has appropriate valididy (u|f)
  66. # - checks key has appropriate capability (E|A)
  67. # - checks that particular desired user id has appropriate validity
  68. # see /usr/share/doc/gnupg/DETAILS.gz
  69. # FIXME: add some more status output
  70. # expects global variable: "mode"
  71. process_user_id() {
  72. local userID
  73. local cacheDir
  74. local keyOK
  75. local keyCapability
  76. local keyFingerprint
  77. local userIDHash
  78. userID="$1"
  79. cacheDir="$2"
  80. # fetch all keys from keyserver
  81. # if none found, break
  82. if ! gpg_fetch_keys "$userID" ; then
  83. echo " no keys found."
  84. return
  85. fi
  86. # some crazy piping here that takes the output of gpg and
  87. # pipes it into a "while read" loop that reads each line
  88. # of standard input one-by-one.
  89. gpg --fixed-list-mode --list-key --with-colons \
  90. --with-fingerprint ="$userID" 2> /dev/null | \
  91. cut -d : -f 1,2,5,10,12 | \
  92. while IFS=: read -r type validity keyid uidfpr capability ; do
  93. # process based on record type
  94. case $type in
  95. 'pub')
  96. # new key, wipe the slate
  97. keyOK=
  98. keyCapability=
  99. keyFingerprint=
  100. # check primary key validity
  101. if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then
  102. continue
  103. fi
  104. # check capability is not Disabled...
  105. if echo "$capability" | grep -q 'D' ; then
  106. continue
  107. fi
  108. # check capability is Encryption and Authentication
  109. # FIXME: make more flexible capability specification
  110. # (ie. in conf file)
  111. if echo "$capability" | grep -q -v 'E' ; then
  112. if echo "$capability" | grep -q -v 'A' ; then
  113. continue
  114. fi
  115. fi
  116. keyCapability="$capability"
  117. keyOK=true
  118. keyID="$keyid"
  119. ;;
  120. 'fpr')
  121. # if key ok, get fingerprint
  122. if [ "$keyOK" ] ; then
  123. keyFingerprint="$uidfpr"
  124. fi
  125. ;;
  126. 'uid')
  127. # check key ok and we have key fingerprint
  128. if [ -z "$keyOK" -o -z "$keyFingerprint" ] ; then
  129. continue
  130. fi
  131. # check key validity
  132. if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then
  133. continue
  134. fi
  135. # check the uid matches
  136. if [ "$(unescape "$uidfpr")" != "$userID" ] ; then
  137. continue
  138. fi
  139. # convert the key
  140. # FIXME: needs to apply extra options if specified
  141. echo -n " valid key found; generating ssh key(s)... "
  142. userIDHash=$(echo "$userID" | sha1sum | awk '{ print $1 }')
  143. # export the key with gpg2ssh
  144. #gpg --export "$keyFingerprint" | gpg2ssh "$mode" > "$cacheDir"/"$userIDHash"."$keyFingerprint"
  145. # stand in until we get dkg's gpg2ssh program
  146. gpg2ssh_tmp "$mode" "$keyID" "$userID" > "$cacheDir"/"$userIDHash"."$keyFingerprint"
  147. if [ "$?" = 0 ] ; then
  148. echo "done."
  149. else
  150. echo "error."
  151. fi
  152. ;;
  153. esac
  154. done
  155. }
  156. # process the auth_*_ids file
  157. # go through line-by-line, extracting and processing each user id
  158. # expects global variable: "mode"
  159. process_auth_file() {
  160. local authIDsFile
  161. local cacheDir
  162. local nLines
  163. local line
  164. local userID
  165. authIDsFile="$1"
  166. cacheDir="$2"
  167. # find number of user ids in auth_user_ids file
  168. nLines=$(meat <"$authIDsFile" | wc -l)
  169. # make sure gpg home exists with proper permissions
  170. mkdir -p -m 0700 "$GNUPGHOME"
  171. # clean out keys file and remake keys directory
  172. rm -rf "$cacheDir"
  173. mkdir -p "$cacheDir"
  174. # loop through all user ids
  175. for line in $(seq 1 $nLines) ; do
  176. # get user id
  177. # FIXME: needs to handle extra options if necessary
  178. userID=$(meat <"$authIDsFile" | cutline "$line" )
  179. # process the user id and extract keys
  180. log "processing user id: '$userID'"
  181. process_user_id "$userID" "$cacheDir"
  182. done
  183. }
  184. ########################################################################
  185. # MAIN
  186. ########################################################################
  187. if [ -z "$1" ] ; then
  188. usage
  189. exit 1
  190. fi
  191. # check mode
  192. mode="$1"
  193. shift 1
  194. # check user
  195. if ! id -u "$USER" > /dev/null 2>&1 ; then
  196. failure "invalid user '$USER'."
  197. fi
  198. # set user home directory
  199. HOME=$(getent passwd "$USER" | cut -d: -f6)
  200. # get ms home directory
  201. MS_HOME=${MS_HOME:-"$HOME"/.config/monkeysphere}
  202. # load configuration file
  203. MS_CONF=${MS_CONF:-"$MS_HOME"/monkeysphere.conf}
  204. [ -e "$MS_CONF" ] && . "$MS_CONF"
  205. # set config variable defaults
  206. STAGING_AREA=${STAGING_AREA:-"$MS_HOME"}
  207. AUTH_HOST_FILE=${AUTH_HOST_FILE:-"$MS_HOME"/auth_host_ids}
  208. AUTH_USER_FILE=${AUTH_USER_FILE:-"$MS_HOME"/auth_user_ids}
  209. GNUPGHOME=${GNUPGHOME:-"$HOME"/.gnupg}
  210. KEYSERVER=${KEYSERVER:-subkeys.pgp.net}
  211. USER_KNOW_HOSTS="$HOME"/.ssh/known_hosts
  212. USER_AUTHORIZED_KEYS="$HOME"/.ssh/authorized_keys
  213. # export USER and GNUPGHOME variables, since they are used by gpg
  214. export USER
  215. export GNUPGHOME
  216. # stagging locations
  217. hostKeysCacheDir="$STAGING_AREA"/host_keys
  218. userKeysCacheDir="$STAGING_AREA"/user_keys
  219. msKnownHosts="$STAGING_AREA"/known_hosts
  220. msAuthorizedKeys="$STAGING_AREA"/authorized_keys
  221. # set mode variables
  222. if [ "$mode" = '--known_hosts' -o "$mode" = '-k' ] ; then
  223. fileType=known_hosts
  224. authIDsFile="$AUTH_HOST_FILE"
  225. outFile="$msKnownHosts"
  226. cacheDir="$hostKeysCacheDir"
  227. userFile="$USER_KNOWN_HOSTS"
  228. elif [ "$mode" = '--authorized_keys' -o "$mode" = '-a' ] ; then
  229. fileType=authorized_keys
  230. authIDsFile="$AUTH_USER_FILE"
  231. outFile="$msAuthorizedKeys"
  232. cacheDir="$userKeysCacheDir"
  233. userFile="$USER_AUTHORIZED_KEYS"
  234. else
  235. failure "unknown command '$mode'."
  236. fi
  237. # check auth ids file
  238. if [ ! -s "$authIDsFile" ] ; then
  239. echo $(basename "$authIDsFile") "file is empty or does not exist."
  240. exit
  241. fi
  242. log "user '$USER': monkeysphere $fileType generation..."
  243. # process the auth file
  244. process_auth_file "$authIDsFile" "$cacheDir"
  245. # write output key file
  246. log "writing ms $fileType file... "
  247. > "$outFile"
  248. if [ "$(ls "$cacheDir")" ] ; then
  249. log -n "adding gpg keys... "
  250. cat "$cacheDir"/* > "$outFile"
  251. echo "done."
  252. else
  253. log "no gpg keys to add."
  254. fi
  255. if [ -s "$userFile" ] ; then
  256. log -n "adding user $fileType file... "
  257. cat "$userFile" >> "$outFile"
  258. echo "done."
  259. fi
  260. log "ms $fileType file generated:"
  261. log "$outFile"