Bug 1157

Summary: ssh-keygen doesn't handle DOS line breaks
Product: Portable OpenSSH Reporter: Chris Pepper <pepper>
Component: ssh-keygenAssignee: Assigned to nobody <unassigned-bugs>
Status: CLOSED FIXED    
Severity: normal    
Priority: P2    
Version: 3.8.1p1   
Hardware: All   
OS: All   
URL: http://openssh.org/txt/draft-ietf-secsh-publickeyfile-02.txt
Bug Depends on:    
Bug Blocks: 1155    
Attachments:
Description Flags
Here's a commercial key with DOS-style line endings, which chokes ssh-keygen
none
Handle keys with CR termination only.
none
Make ssh-keygen accept CR, LF or CRLF in keys.
none
Make ssh-keygen accept CR, LF or CRLF in keys take 2.
none
Make ssh-keygen accept CR, LF or CRLF in keys take 3.
none
A sample key with Mac line breaks
none
Make ssh-keygen accept CR, LF or CRLF in keys take 4
none
Make ssh-keygen accept CR, LF or CRLF in keys take 5, with feedback from djm
djm: ok+
Regress test for this (against -portable) djm: ok+

Description Chris Pepper 2006-02-15 08:16:28 AEDT
ssh-keygen cannot import files with DOS-style line endings (CR/LF). This appears to be a violation of draft-ietf-secsh-publickeyfile-02, which says all line-break styles MUST be supported.

I initially noticed this with a key created on VMS, using DOS-style line breaks, and confirmed with a brand-new key created with ssh-3.2.9.1. ssh-keygen was able to import the key with default UNIX-style line endings, but I got "uudecode failed." with DOS-style line breaks.


pepper@salt:~/.ssh2$ ssh-keygen -i -f id_dsa_2048_a.pub.dos 
uudecode failed.


3.1 Line termination Characters

   In order to achieve the goal of being able to exchange public key
   files between servers, implementations are REQUIRED to read files
   using any of the common line termination sequence, <CR>, <LF> or
   <CR><LF>.

   Implementations may generate files using which ever line termination
   convention is most convenient


My system is:

pepper@salt:~/.ssh$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.4.4
BuildVersion:   8G32
pepper@salt:~/.ssh$ uname -a
Darwin salt.rockefeller.edu 8.4.0 Darwin Kernel Version 8.4.0: Tue Jan  3 18:22:10 PST 2006; root:xnu-792.6.56.obj~1/RELEASE_PPC Power Macintosh powerpc
Comment 1 Chris Pepper 2006-02-15 08:20:44 AEDT
Created attachment 1067 [details]
Here's a commercial key with DOS-style line endings, which chokes ssh-keygen
Comment 2 Darren Tucker 2006-02-15 12:36:45 AEDT
Created attachment 1068 [details]
Handle keys with CR termination only.

Please try this patch.
Comment 3 Darren Tucker 2006-02-15 12:51:56 AEDT
Comment on attachment 1068 [details]
Handle keys with CR termination only.

Ignore this patch, it breaks other key types.
Comment 4 Darren Tucker 2006-02-15 14:38:22 AEDT
Created attachment 1069 [details]
Make ssh-keygen accept CR, LF or CRLF in keys.

Please try this patch instead.
Comment 5 Chris Pepper 2006-02-15 15:18:47 AEDT
This works for the DOS (CR/LF) case, and still works for the UNIX (LF) case, but doesn't work for the (Classic) Mac (CR) case.
Comment 6 Darren Tucker 2006-02-15 20:06:33 AEDT
Created attachment 1070 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 2.

Alright, please try this one.  It's not all that elegant because fgets slurps the whole thing into the buffer when it doesn't see what it considers a newline so it seeks back for each subsequent line.

The fgets probably wants replacing with a variant of fgetln that accepts all of CR, LF and CRLF.
Comment 7 Damien Miller 2006-02-15 22:50:20 AEDT
Comment on attachment 1069 [details]
Make ssh-keygen accept CR, LF or CRLF in keys.

> 	while (fgets(line, sizeof(line), fp)) {
>-		if (!(p = strchr(line, '\n'))) {
>+		p = strchr(line, '\n');
>+		q = strchr(line, '\r');


Why not just:

if ((p = strchr(line, '\n')) != NULL)
        *p = '\0';
if ((p = strchr(line, '\r')) != NULL)
        *p = '\0';
Comment 8 Darren Tucker 2006-02-15 23:00:50 AEDT
(In reply to comment #7)
> Why not just:
> 
> if ((p = strchr(line, '\n')) != NULL)
>         *p = '\0';
> if ((p = strchr(line, '\r')) != NULL)
>         *p = '\0';

Well, for one reason, if there's no '\r' in the string, p will now be NULL, and then when we deref it...
Comment 9 Darren Tucker 2006-02-15 23:06:23 AEDT
(In reply to comment #8)
> Well, for one reason, if there's no '\r' in the string, p will now be NULL, and
> then when we deref it...

... although this:
      if (p > line && p[-1] == '\\')
could be restructured to this:
      len = strlen(line);
      if (len > 0 && line[len - 1] == '\\')
which might even work :-)
Comment 10 Chris Pepper 2006-02-21 17:14:33 AEDT
Do y'all have a consensus on the preferred patch I should test?
Comment 11 Darren Tucker 2006-02-21 21:41:54 AEDT
Created attachment 1074 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 3.
Comment 12 Chris Pepper 2006-02-23 02:20:32 AEDT
Created attachment 1078 [details]
A sample key with Mac line breaks

With patch #1074, ssh-keygen -i fails on this file.
Comment 13 Chris Pepper 2006-02-23 02:22:45 AEDT
Patch 1074 works on UNIX & DOS files, but breaks in the Mac case ("unget error").

pepper@pepperbook:~/port/openssh$ ./ssh-keygen -i -f ~/Desktop/ssh-keygen-bug/id_dsa_2048_a.pub.mac
unget: Unknown error: 0

I attached a sample key with Mac line breaks.
ssh-keygen -i also breaks on < (with or without "-f -"). Is this intended to work?
Comment 14 Darren Tucker 2006-02-26 16:32:36 AEDT
Created attachment 1086 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 4

OK, this one works with the sample key.  Sooner or later I'll run out of wrong ways to do this :-)

As far as redirection with "-", it's not a supported syntax (for the most part, it works with -T but nothing else).  You can try "ssh-keygen -i -f /dev/stdin", assuming your OS supports it.
Comment 15 Chris Pepper 2006-02-27 12:48:27 AEDT
The latest patch ("take 4") works for all 3 cases (UNIX, DOS, & Mac). Thanks!
Comment 16 Damien Miller 2006-02-27 22:29:00 AEDT
Comment on attachment 1086 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 4

Looks good, a couple of nits:

>+static int
>+get_line(FILE *fp, char *line, size_t len)
>+{
>+	int c;
>+	size_t pos = 0;
>+
>+	if (len &gt; INT_MAX)
>+		return -1;

I don't think this is necessary in the context.

>+		line[pos++] = c;
>+		line[pos] = '\0';
>+	}
>+	return 0;

Maybe this function should return the length of the string, that would save the strlen() later:

>+	while (get_line(fp, line, sizeof(line)) == 0) {
>+		len = strlen(line);
>+		if (line[len - 1] == '\\')
> 			escaped++;

Regress test? :)
Comment 17 Darren Tucker 2006-02-27 22:47:20 AEDT
(In reply to comment #16)
> >+	if (len &gt; INT_MAX)
> >+		return -1;
> 
> I don't think this is necessary in the context.

I was being paranoid.

> Maybe this function should return the length of the string, that would
> save the strlen() later:

Good idea, I'll change it.

> Regress test? :)

Some people are never happy :-)  Actually I already have one but posting it as a diff is useless because of the line-break differences in the test data.
Comment 18 Darren Tucker 2006-02-27 23:23:26 AEDT
Created attachment 1087 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 5, with feedback from djm
Comment 19 Chris Pepper 2006-02-28 12:45:22 AEDT
(In reply to comment #18)
> Created an attachment (id=1087) [edit]
> Make ssh-keygen accept CR, LF or CRLF in keys take 5, with feedback from djm

Where do I get v1.141, which patch #5 is against? I don't see it at <http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/ssh-keygen.c>, and the patch doesn't apply against v1.135 from
openssh-4.3.tar.gz or openssh-SNAP-20060213.tar.gz.


Thanks,
Chris
Comment 20 Darren Tucker 2006-02-28 12:51:05 AEDT
(In reply to comment #19)
> Where do I get v1.141, which patch #5 is against?

Try: http://cvsweb.mindrot.org/index.cgi/openssh/ssh-keygen.c

Not sure why it's not in the snaps, though.
Comment 21 Darren Tucker 2006-03-07 23:44:01 AEDT
Created attachment 1091 [details]
Regress test for this (against -portable)

In reply to comment #16)
> Regress test? :)

Happy now?  :-)
Comment 22 Damien Miller 2006-03-12 15:36:18 AEDT
Comment on attachment 1087 [details]
Make ssh-keygen accept CR, LF or CRLF in keys take 5, with feedback from djm

looks sane
Comment 23 Damien Miller 2006-03-12 15:39:26 AEDT
Comment on attachment 1091 [details]
Regress test for this (against -portable)

ok
Comment 24 Darren Tucker 2006-03-13 19:50:58 AEDT
Thanks all.  Patches #1087 and #1091 have been applied and will be in 4.4.
Comment 25 Darren Tucker 2006-09-28 19:25:49 AEST
With the release of 4.4, we believe that this bug is now closed.  For information about the release please see http://www.openssh.com/txt/release-4.4 .