0

Could it be automated via ksh/bash, so via a schellscript to check all users ~/.ssh/authorized_keys file for bad = or == ending?

One of my friend deleted the = and the == from the end of the SSH keys, so users got locked out, because that was the part of their key :)

pattern it went from this (it could be ssh-rsa and with different key length):

from="1.2.3.4" ssh-dss AAAAB....0bOJKs= COMMENTHERE COMMENTHERE

to this:

from="1.2.3.4" ssh-dss AAAAB....0bOJKs COMMENTHERE COMMENTHERE

example solution: is there a fix length for the keys? how to filter out the bad keys?

  • 6
    I would say string must be multiple of 4, either one or two = – Archemar Sep 16 '16 at 9:22
  • 1
    What is your question? If you can automate fixing them? Sure you can. – Num Lock Sep 16 '16 at 11:26
  • 2
    @NumLock, that's like the mathematicians answer from the joke: completely accurate, and totally useless... – ilkkachu Sep 16 '16 at 11:47
  • 1
    @ilkkachu Well, to all other questions OP was asking he already stated in the comments (of the top voted answer) that he already knows everything about it. So yeah, I don't get what the question is. If he wants to know how to automate this, I would have expected some initial effort. That's common for StackExchange btw. – Num Lock Sep 19 '16 at 7:27
23

The = mark is just padding, to fill out a base64 conversion. You can read more about that in

You could automate a fix/check for this because the total number of characters in a base64 value (disregarding those outside the encoding such as whitespace) would be a multiple of 4.

| improve this answer | |
  • that wasn't my question. I know about it, but my friend just learnt that :) – Peter84753 Sep 16 '16 at 9:19
  • 12
    @Peter84753 Actually that was exactly your question. "is there a fix length for the keys?". Regarding that other question "how to filter out the bad keys?" and since you apparently know about the sizing: What have been your efforts towards solving that? Your question doesn't show any efforts. – Num Lock Sep 16 '16 at 11:24
  • 2
    @Peter84753 By the way, the answer to "Could it be automated via ksh/bash, so via a schellscript to check all users ~/.ssh/authorized_keys file for bad = or == ending?" is yes. – Num Lock Sep 16 '16 at 11:25
4

A quick fix in Perl:

perl -lane '$a = -1; for(0..$#F) {$a = $_ + 1 if $F[$_] =~ /^ssh-|^ecdsa-/; };
    die if $a == -1;  $p = (4 - length($F[$a]) % 4) % 4; 
    $F[$a] .= "=" x $p;  print join " ", @F' < authorized_keys > authorized_keys2

For each line (-n) Autosplit (-a) the fields to @F along spaces, then find the field that contains the key type, the next is the key. Subtract from 4 the length modulo 4 to get the number of = signs we need to add, except take another modulo to turn a 4 to a 0 in case the field is already the correct length.

The manual says that

Protocol 2 public key consist of: options, keytype, base64-encoded key, comment. The options field is optional; its presence is determined by whether the line starts with a number or not

but that's not quite right since v2 key types don't start with numbers either. The options field an contain spaces so the position of the key type can be anything. We should actually parse the options to find any quoted strings, but heuristically looking for the known key types should do.

| improve this answer | |
  • 1
    If you used (4 - length($F[$a]) % 4) % 4 it would not add unnecessary 4 equal signs in the first place :-) – Bergi Sep 16 '16 at 11:24
  • @Bergi, actually I did that first, then decided the alternative may be simpler to understand. (maybe it's just me) – ilkkachu Sep 16 '16 at 11:51
  • It isn't. Either way you have to compute a length modulo 4. – Marquis of Lorne Sep 17 '16 at 0:17
0
cut -d: -f6 /etc/passwd | 
  while read oneuserraw; do
    if [ -s "${oneuserraw}/.ssh/authorized_keys" ]; then
      echo "${oneuserraw}/.ssh/authorized_keys"
    fi
  done |
  perl -pe 's/\/\//\//g' |
  while read oneuser; do
    echo checking: "$oneuser"
    cat "$oneuser" | while read oneline; do
      if [[ "$oneline" == from* ]]; then
        key=$(echo "$oneline" | cut -d' ' -f3)
      fi
      if [[ "$oneline" == ssh* ]]; then
        key=$(echo "$oneline" | cut -d' ' -f2)
      fi
      length=$(echo "$key" | awk '{ print length }')
      if ! (( $length % 4 == 0 )); then
        echo "$oneline"
      fi
    done
  done

I wrote a checker myself too.

This even throws out the bad lines where someone hit enter at the ex.: middle of the ssh key AND not divisible with 4.

tested on AIX/Linux so ksh and bash.

| improve this answer | |
  • 5
    Just a point on presentation: with a script of that length, you may want to split to several lines, as SE doesn't split lines on code blocks (luckily). It's a bit hard to read a single line of 300 characters. – ilkkachu Sep 16 '16 at 11:49
  • 1
    There's a lot about this code that's very unnecessarily inefficient. ${#key} gives the length of that key, for instance. read _ key _ <<<"$oneline" reads the second whitespace-separated item from oneline into the variable named key without needing a subprocess and a tool like cut. You could use the same tool to not need cut to read the password file: while IFS=: read name password uid gid class change expire gecos home_dir shell _; do ...; done </etc/passwd, for instance, reads each field from /etc/passwd into a different shell variable using only builtins. – Charles Duffy Sep 16 '16 at 16:25
  • Also, consider using a case statement rather than multiple if statements -- that gives you equivalent behavior to your if [[ $oneline = from* ]] without depending on non-POSIX behavior. – Charles Duffy Sep 16 '16 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.