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