2002-05-11 20:45:29 +04:00
|
|
|
/*
|
|
|
|
* Code for PuTTY to import and export private key files in other
|
|
|
|
* SSH clients' formats.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
2003-02-01 15:54:40 +03:00
|
|
|
#include "putty.h"
|
2002-05-11 20:45:29 +04:00
|
|
|
#include "ssh.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_pem_encrypted(const Filename *file);
|
|
|
|
static bool openssh_new_encrypted(const Filename *file);
|
|
|
|
static struct ssh2_userkey *openssh_pem_read(
|
|
|
|
const Filename *file, const char *passphrase, const char **errmsg_p);
|
|
|
|
static struct ssh2_userkey *openssh_new_read(
|
|
|
|
const Filename *file, const char *passphrase, const char **errmsg_p);
|
|
|
|
static bool openssh_auto_write(
|
|
|
|
const Filename *file, struct ssh2_userkey *key, const char *passphrase);
|
|
|
|
static bool openssh_pem_write(
|
|
|
|
const Filename *file, struct ssh2_userkey *key, const char *passphrase);
|
|
|
|
static bool openssh_new_write(
|
|
|
|
const Filename *file, struct ssh2_userkey *key, const char *passphrase);
|
|
|
|
|
|
|
|
static bool sshcom_encrypted(const Filename *file, char **comment);
|
|
|
|
static struct ssh2_userkey *sshcom_read(
|
|
|
|
const Filename *file, const char *passphrase, const char **errmsg_p);
|
|
|
|
static bool sshcom_write(
|
|
|
|
const Filename *file, struct ssh2_userkey *key, const char *passphrase);
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2002-05-11 20:45:29 +04:00
|
|
|
/*
|
|
|
|
* Given a key type, determine whether we know how to import it.
|
|
|
|
*/
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool import_possible(int type)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:46:58 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_PEM)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return true;
|
2015-04-28 21:46:58 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_NEW)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return true;
|
2002-05-13 20:32:42 +04:00
|
|
|
if (type == SSH_KEYTYPE_SSHCOM)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return true;
|
|
|
|
return false;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a key type, determine what native key type
|
|
|
|
* (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once
|
|
|
|
* we've imported it.
|
|
|
|
*/
|
|
|
|
int import_target_type(int type)
|
|
|
|
{
|
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* There are no known foreign SSH-1 key formats.
|
2002-05-11 20:45:29 +04:00
|
|
|
*/
|
|
|
|
return SSH_KEYTYPE_SSH2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine whether a foreign key is encrypted.
|
|
|
|
*/
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool import_encrypted(const Filename *filename, int type, char **comment)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:49:55 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_PEM) {
|
|
|
|
/* OpenSSH PEM format doesn't contain a key comment at all */
|
2003-02-01 20:24:27 +03:00
|
|
|
*comment = dupstr(filename_to_str(filename));
|
2015-04-28 21:49:55 +03:00
|
|
|
return openssh_pem_encrypted(filename);
|
|
|
|
} else if (type == SSH_KEYTYPE_OPENSSH_NEW) {
|
|
|
|
/* OpenSSH new format does, but it's inside the encrypted
|
|
|
|
* section for some reason */
|
|
|
|
*comment = dupstr(filename_to_str(filename));
|
|
|
|
return openssh_new_encrypted(filename);
|
|
|
|
} else if (type == SSH_KEYTYPE_SSHCOM) {
|
2002-05-13 20:32:42 +04:00
|
|
|
return sshcom_encrypted(filename, comment);
|
|
|
|
}
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return false;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* Import an SSH-1 key.
|
2002-05-11 20:45:29 +04:00
|
|
|
*/
|
2003-02-01 15:54:40 +03:00
|
|
|
int import_ssh1(const Filename *filename, int type,
|
2005-02-28 02:01:11 +03:00
|
|
|
struct RSAKey *key, char *passphrase, const char **errmsg_p)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* Import an SSH-2 key.
|
2002-05-11 20:45:29 +04:00
|
|
|
*/
|
2003-02-01 15:54:40 +03:00
|
|
|
struct ssh2_userkey *import_ssh2(const Filename *filename, int type,
|
2005-02-28 02:01:11 +03:00
|
|
|
char *passphrase, const char **errmsg_p)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:49:55 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_PEM)
|
|
|
|
return openssh_pem_read(filename, passphrase, errmsg_p);
|
|
|
|
else if (type == SSH_KEYTYPE_OPENSSH_NEW)
|
|
|
|
return openssh_new_read(filename, passphrase, errmsg_p);
|
2002-05-13 20:32:42 +04:00
|
|
|
if (type == SSH_KEYTYPE_SSHCOM)
|
2005-02-28 02:01:11 +03:00
|
|
|
return sshcom_read(filename, passphrase, errmsg_p);
|
2002-05-11 20:45:29 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-05-13 20:56:11 +04:00
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* Export an SSH-1 key.
|
2002-05-13 20:56:11 +04:00
|
|
|
*/
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool export_ssh1(const Filename *filename, int type, struct RSAKey *key,
|
|
|
|
char *passphrase)
|
2002-05-13 20:56:11 +04:00
|
|
|
{
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return false;
|
2002-05-13 20:56:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* Export an SSH-2 key.
|
2002-05-13 20:56:11 +04:00
|
|
|
*/
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool export_ssh2(const Filename *filename, int type,
|
|
|
|
struct ssh2_userkey *key, char *passphrase)
|
2002-05-13 20:56:11 +04:00
|
|
|
{
|
2015-05-10 09:42:48 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_AUTO)
|
|
|
|
return openssh_auto_write(filename, key, passphrase);
|
2015-04-28 21:46:58 +03:00
|
|
|
if (type == SSH_KEYTYPE_OPENSSH_NEW)
|
|
|
|
return openssh_new_write(filename, key, passphrase);
|
2002-05-13 20:56:11 +04:00
|
|
|
if (type == SSH_KEYTYPE_SSHCOM)
|
|
|
|
return sshcom_write(filename, key, passphrase);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return false;
|
2002-05-13 20:56:11 +04:00
|
|
|
}
|
|
|
|
|
2007-01-07 17:20:28 +03:00
|
|
|
/*
|
|
|
|
* Strip trailing CRs and LFs at the end of a line of text.
|
|
|
|
*/
|
|
|
|
void strip_crlf(char *str)
|
|
|
|
{
|
|
|
|
char *p = str + strlen(str);
|
|
|
|
|
|
|
|
while (p > str && (p[-1] == '\r' || p[-1] == '\n'))
|
|
|
|
*--p = '\0';
|
|
|
|
}
|
|
|
|
|
2002-05-11 20:45:29 +04:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* Helper routines. (The base64 ones are defined in sshpubk.c.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \
|
|
|
|
((c) >= 'a' && (c) <= 'z') || \
|
|
|
|
((c) >= '0' && (c) <= '9') || \
|
|
|
|
(c) == '+' || (c) == '/' || (c) == '=' \
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read an ASN.1/BER identifier and length pair.
|
|
|
|
*
|
|
|
|
* Flags are a combination of the #defines listed below.
|
|
|
|
*
|
|
|
|
* Returns -1 if unsuccessful; otherwise returns the number of
|
|
|
|
* bytes used out of the source data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* ASN.1 tag classes. */
|
|
|
|
#define ASN1_CLASS_UNIVERSAL (0 << 6)
|
|
|
|
#define ASN1_CLASS_APPLICATION (1 << 6)
|
|
|
|
#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
|
|
|
|
#define ASN1_CLASS_PRIVATE (3 << 6)
|
|
|
|
#define ASN1_CLASS_MASK (3 << 6)
|
|
|
|
|
|
|
|
/* Primitive versus constructed bit. */
|
|
|
|
#define ASN1_CONSTRUCTED (1 << 5)
|
|
|
|
|
2002-05-14 22:11:15 +04:00
|
|
|
/*
|
|
|
|
* Write an ASN.1/BER identifier and length pair. Returns the
|
|
|
|
* number of bytes consumed. Assumes dest contains enough space.
|
|
|
|
* Will avoid writing anything if dest is NULL, but still return
|
|
|
|
* amount of space required.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
static void BinarySink_put_ber_id_len(BinarySink *bs,
|
|
|
|
int id, int length, int flags)
|
2002-05-14 22:11:15 +04:00
|
|
|
{
|
|
|
|
if (id <= 30) {
|
|
|
|
/*
|
|
|
|
* Identifier is one byte.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
put_byte(bs, id | flags);
|
2002-05-14 22:11:15 +04:00
|
|
|
} else {
|
|
|
|
int n;
|
|
|
|
/*
|
|
|
|
* Identifier is multiple bytes: the first byte is 11111
|
|
|
|
* plus the flags, and subsequent bytes encode the value of
|
|
|
|
* the identifier, 7 bits at a time, with the top bit of
|
|
|
|
* each byte 1 except the last one which is 0.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
put_byte(bs, 0x1F | flags);
|
2002-05-14 22:11:15 +04:00
|
|
|
for (n = 1; (id >> (7*n)) > 0; n++)
|
|
|
|
continue; /* count the bytes */
|
2018-05-24 15:11:56 +03:00
|
|
|
while (n--)
|
|
|
|
put_byte(bs, (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F));
|
2002-05-14 22:11:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (length < 128) {
|
|
|
|
/*
|
|
|
|
* Length is one byte.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
put_byte(bs, length);
|
2002-05-14 22:11:15 +04:00
|
|
|
} else {
|
|
|
|
int n;
|
|
|
|
/*
|
|
|
|
* Length is multiple bytes. The first is 0x80 plus the
|
|
|
|
* number of subsequent bytes, and the subsequent bytes
|
|
|
|
* encode the actual length.
|
|
|
|
*/
|
|
|
|
for (n = 1; (length >> (8*n)) > 0; n++)
|
|
|
|
continue; /* count the bytes */
|
2018-05-24 15:11:56 +03:00
|
|
|
put_byte(bs, 0x80 | n);
|
|
|
|
while (n--)
|
|
|
|
put_byte(bs, (length >> (8*n)) & 0xFF);
|
2002-05-14 22:11:15 +04:00
|
|
|
}
|
2015-04-28 21:51:52 +03:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
#define put_ber_id_len(bs, id, len, flags) \
|
|
|
|
BinarySink_put_ber_id_len(BinarySink_UPCAST(bs), id, len, flags)
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
typedef struct ber_item {
|
|
|
|
int id;
|
|
|
|
int flags;
|
|
|
|
ptrlen data;
|
|
|
|
} ber_item;
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
static ber_item BinarySource_get_ber(BinarySource *src)
|
2002-05-14 22:11:15 +04:00
|
|
|
{
|
2018-05-28 19:42:03 +03:00
|
|
|
ber_item toret;
|
|
|
|
unsigned char leadbyte, lenbyte;
|
|
|
|
size_t length;
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
leadbyte = get_byte(src);
|
|
|
|
toret.flags = (leadbyte & 0xE0);
|
|
|
|
if ((leadbyte & 0x1F) == 0x1F) {
|
|
|
|
unsigned char idbyte;
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
toret.id = 0;
|
|
|
|
do {
|
|
|
|
idbyte = get_byte(src);
|
|
|
|
toret.id = (toret.id << 7) | (idbyte & 0x7F);
|
|
|
|
} while (idbyte & 0x80);
|
|
|
|
} else {
|
|
|
|
toret.id = leadbyte & 0x1F;
|
|
|
|
}
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
lenbyte = get_byte(src);
|
|
|
|
if (lenbyte & 0x80) {
|
|
|
|
int nbytes = lenbyte & 0x7F;
|
|
|
|
length = 0;
|
|
|
|
while (nbytes-- > 0)
|
|
|
|
length = (length << 8) | get_byte(src);
|
|
|
|
} else {
|
|
|
|
length = lenbyte;
|
|
|
|
}
|
|
|
|
|
|
|
|
toret.data = get_data(src, length);
|
|
|
|
return toret;
|
2002-05-14 22:11:15 +04:00
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
#define get_ber(bs) BinarySource_get_ber(BinarySource_UPCAST(bs))
|
|
|
|
|
2002-05-11 20:45:29 +04:00
|
|
|
/* ----------------------------------------------------------------------
|
2015-04-28 21:49:55 +03:00
|
|
|
* Code to read and write OpenSSH private keys, in the old-style PEM
|
|
|
|
* format.
|
2002-05-11 20:45:29 +04:00
|
|
|
*/
|
|
|
|
|
2015-04-27 22:48:29 +03:00
|
|
|
typedef enum {
|
2015-04-28 21:49:55 +03:00
|
|
|
OP_DSA, OP_RSA, OP_ECDSA
|
|
|
|
} openssh_pem_keytype;
|
2015-04-27 22:48:29 +03:00
|
|
|
typedef enum {
|
2015-04-28 21:49:55 +03:00
|
|
|
OP_E_3DES, OP_E_AES
|
|
|
|
} openssh_pem_enc;
|
2015-04-27 22:48:29 +03:00
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
struct openssh_pem_key {
|
|
|
|
openssh_pem_keytype keytype;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool encrypted;
|
2015-04-28 21:49:55 +03:00
|
|
|
openssh_pem_enc encryption;
|
|
|
|
char iv[32];
|
2018-05-28 19:42:03 +03:00
|
|
|
strbuf *keyblob;
|
2002-05-11 20:45:29 +04:00
|
|
|
};
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
void BinarySink_put_mp_ssh2_from_string(
|
|
|
|
BinarySink *bs, const void *bytesv, int nbytes)
|
|
|
|
{
|
|
|
|
const unsigned char *bytes = (const unsigned char *)bytesv;
|
|
|
|
while (nbytes > 0 && bytes[0] == 0) {
|
|
|
|
nbytes--;
|
|
|
|
bytes++;
|
|
|
|
}
|
|
|
|
if (nbytes > 0 && bytes[0] & 0x80) {
|
|
|
|
put_uint32(bs, nbytes + 1);
|
|
|
|
put_byte(bs, 0);
|
|
|
|
} else {
|
|
|
|
put_uint32(bs, nbytes);
|
|
|
|
}
|
|
|
|
put_data(bs, bytes, nbytes);
|
|
|
|
}
|
|
|
|
#define put_mp_ssh2_from_string(bs, val, len) \
|
|
|
|
BinarySink_put_mp_ssh2_from_string(BinarySink_UPCAST(bs), val, len)
|
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
|
|
|
|
const char **errmsg_p)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:49:55 +03:00
|
|
|
struct openssh_pem_key *ret;
|
2012-08-30 22:44:33 +04:00
|
|
|
FILE *fp = NULL;
|
2007-01-07 17:20:28 +03:00
|
|
|
char *line = NULL;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
|
|
|
char *p;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool headers_done;
|
2002-05-13 20:37:11 +04:00
|
|
|
char base64_bit[4];
|
|
|
|
int base64_chars = 0;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
ret = snew(struct openssh_pem_key);
|
2018-05-28 19:42:03 +03:00
|
|
|
ret->keyblob = strbuf_new();
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "r", false);
|
2002-05-11 20:45:29 +04:00
|
|
|
if (!fp) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unable to open key file";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
|
|
|
|
if (!(line = fgetline(fp))) {
|
|
|
|
errmsg = "unexpected end of file";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
strip_crlf(line);
|
2015-11-10 21:47:55 +03:00
|
|
|
if (!strstartswith(line, "-----BEGIN ") ||
|
|
|
|
!strendswith(line, "PRIVATE KEY-----")) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "file does not begin with OpenSSH key header";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2015-04-27 22:48:29 +03:00
|
|
|
/*
|
|
|
|
* Parse the BEGIN line. For old-format keys, this tells us the
|
|
|
|
* type of the key; for new-format keys, all it tells us is the
|
|
|
|
* format, and we'll find out the key type once we parse the
|
|
|
|
* base64.
|
|
|
|
*/
|
|
|
|
if (!strcmp(line, "-----BEGIN RSA PRIVATE KEY-----")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->keytype = OP_RSA;
|
2015-04-27 22:48:29 +03:00
|
|
|
} else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->keytype = OP_DSA;
|
2015-04-27 22:48:29 +03:00
|
|
|
} else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->keytype = OP_ECDSA;
|
2015-04-27 22:48:29 +03:00
|
|
|
} else if (!strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "this is a new-style OpenSSH key";
|
|
|
|
goto error;
|
2015-04-27 22:48:29 +03:00
|
|
|
} else {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unrecognised key type";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2018-10-29 22:50:29 +03:00
|
|
|
ret->encrypted = false;
|
2015-04-28 21:49:55 +03:00
|
|
|
memset(ret->iv, 0, sizeof(ret->iv));
|
2015-04-27 22:48:29 +03:00
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
headers_done = false;
|
2002-05-11 20:45:29 +04:00
|
|
|
while (1) {
|
2007-01-07 17:20:28 +03:00
|
|
|
if (!(line = fgetline(fp))) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unexpected end of file";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
strip_crlf(line);
|
2015-11-10 21:47:55 +03:00
|
|
|
if (strstartswith(line, "-----END ") &&
|
|
|
|
strendswith(line, "PRIVATE KEY-----")) {
|
2013-07-14 14:46:07 +04:00
|
|
|
sfree(line);
|
2013-07-20 17:15:11 +04:00
|
|
|
line = NULL;
|
2002-05-11 20:45:29 +04:00
|
|
|
break; /* done */
|
2013-07-14 14:46:07 +04:00
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
if ((p = strchr(line, ':')) != NULL) {
|
2002-05-11 20:45:29 +04:00
|
|
|
if (headers_done) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "header found in body of key data";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*p++ = '\0';
|
|
|
|
while (*p && isspace((unsigned char)*p)) p++;
|
2007-01-07 17:20:28 +03:00
|
|
|
if (!strcmp(line, "Proc-Type")) {
|
2002-05-11 20:45:29 +04:00
|
|
|
if (p[0] != '4' || p[1] != ',') {
|
|
|
|
errmsg = "Proc-Type is not 4 (only 4 is supported)";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
p += 2;
|
2007-01-07 17:20:28 +03:00
|
|
|
if (!strcmp(p, "ENCRYPTED"))
|
2018-10-29 22:50:29 +03:00
|
|
|
ret->encrypted = true;
|
2007-01-07 17:20:28 +03:00
|
|
|
} else if (!strcmp(line, "DEK-Info")) {
|
2017-06-20 09:05:39 +03:00
|
|
|
int i, ivlen;
|
2010-04-12 14:55:31 +04:00
|
|
|
|
|
|
|
if (!strncmp(p, "DES-EDE3-CBC,", 13)) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->encryption = OP_E_3DES;
|
2010-04-12 14:55:31 +04:00
|
|
|
ivlen = 8;
|
|
|
|
} else if (!strncmp(p, "AES-128-CBC,", 12)) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->encryption = OP_E_AES;
|
2010-04-12 14:55:31 +04:00
|
|
|
ivlen = 16;
|
|
|
|
} else {
|
|
|
|
errmsg = "unsupported cipher";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2010-04-12 14:55:31 +04:00
|
|
|
p = strchr(p, ',') + 1;/* always non-NULL, by above checks */
|
|
|
|
for (i = 0; i < ivlen; i++) {
|
2017-06-20 09:05:39 +03:00
|
|
|
unsigned j;
|
2010-04-12 14:55:31 +04:00
|
|
|
if (1 != sscanf(p, "%2x", &j)) {
|
|
|
|
errmsg = "expected more iv data in DEK-Info";
|
|
|
|
goto error;
|
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->iv[i] = j;
|
2002-05-11 20:45:29 +04:00
|
|
|
p += 2;
|
|
|
|
}
|
2010-04-12 14:55:31 +04:00
|
|
|
if (*p) {
|
|
|
|
errmsg = "more iv data than expected in DEK-Info";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
headers_done = true;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2007-01-07 17:20:28 +03:00
|
|
|
p = line;
|
2002-05-13 20:37:11 +04:00
|
|
|
while (isbase64(*p)) {
|
|
|
|
base64_bit[base64_chars++] = *p;
|
|
|
|
if (base64_chars == 4) {
|
|
|
|
unsigned char out[3];
|
|
|
|
int len;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2002-05-13 20:37:11 +04:00
|
|
|
base64_chars = 0;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2002-05-13 20:37:11 +04:00
|
|
|
len = base64_decode_atom(base64_bit, out);
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2002-05-13 20:37:11 +04:00
|
|
|
if (len <= 0) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "invalid base64 encoding";
|
2002-05-13 20:37:11 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
put_data(ret->keyblob, out, len);
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(out, sizeof(out));
|
2002-05-13 20:37:11 +04:00
|
|
|
}
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2002-05-13 20:37:11 +04:00
|
|
|
p++;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
|
2012-08-30 22:44:33 +04:00
|
|
|
fclose(fp);
|
|
|
|
fp = NULL;
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (!ret->keyblob || ret->keyblob->len == 0) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "key body not present";
|
2002-05-11 20:45:29 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (ret->encrypted && ret->keyblob->len % 8 != 0) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "encrypted key blob is not a multiple of "
|
|
|
|
"cipher block size";
|
|
|
|
goto error;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(base64_bit, sizeof(base64_bit));
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = NULL;
|
2002-05-11 20:45:29 +04:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
2007-01-07 17:20:28 +03:00
|
|
|
if (line) {
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(base64_bit, sizeof(base64_bit));
|
2002-05-11 20:45:29 +04:00
|
|
|
if (ret) {
|
2018-05-28 19:42:03 +03:00
|
|
|
if (ret->keyblob)
|
|
|
|
strbuf_free(ret->keyblob);
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(ret, sizeof(*ret));
|
2002-05-11 20:45:29 +04:00
|
|
|
sfree(ret);
|
|
|
|
}
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
2012-08-30 22:44:33 +04:00
|
|
|
if (fp) fclose(fp);
|
2002-05-11 20:45:29 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_pem_encrypted(const Filename *filename)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:49:55 +03:00
|
|
|
struct openssh_pem_key *key = load_openssh_pem_key(filename, NULL);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool ret;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
|
|
|
if (!key)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return false;
|
2002-05-11 20:45:29 +04:00
|
|
|
ret = key->encrypted;
|
2018-05-28 19:42:03 +03:00
|
|
|
strbuf_free(key->keyblob);
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(key, sizeof(*key));
|
2002-05-11 20:45:29 +04:00
|
|
|
sfree(key);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static struct ssh2_userkey *openssh_pem_read(
|
|
|
|
const Filename *filename, const char *passphrase, const char **errmsg_p)
|
2002-05-11 20:45:29 +04:00
|
|
|
{
|
2015-04-28 21:49:55 +03:00
|
|
|
struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p);
|
2002-05-11 20:45:29 +04:00
|
|
|
struct ssh2_userkey *retkey;
|
2018-06-03 14:58:05 +03:00
|
|
|
const ssh_keyalg *alg;
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
2002-05-11 20:45:29 +04:00
|
|
|
int i, num_integers;
|
|
|
|
struct ssh2_userkey *retval = NULL;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *blob = strbuf_new();
|
|
|
|
int privptr = 0, publen;
|
2018-05-28 19:42:03 +03:00
|
|
|
const char *modptr = NULL;
|
2004-01-22 21:52:49 +03:00
|
|
|
int modlen = 0;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (key->encrypted) {
|
2015-04-27 22:48:29 +03:00
|
|
|
/*
|
2015-04-28 21:49:55 +03:00
|
|
|
* Derive encryption key from passphrase and iv/salt:
|
|
|
|
*
|
|
|
|
* - let block A equal MD5(passphrase || iv)
|
|
|
|
* - let block B equal MD5(A || passphrase || iv)
|
|
|
|
* - block C would be MD5(B || passphrase || iv) and so on
|
|
|
|
* - encryption key is the first N bytes of A || B
|
2015-04-27 22:48:29 +03:00
|
|
|
*
|
2015-04-28 21:49:55 +03:00
|
|
|
* (Note that only 8 bytes of the iv are used for key
|
|
|
|
* derivation, even when the key is encrypted with AES and
|
|
|
|
* hence there are 16 bytes available.)
|
|
|
|
*/
|
|
|
|
struct MD5Context md5c;
|
|
|
|
unsigned char keybuf[32];
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, key->iv, 8);
|
2015-04-28 21:49:55 +03:00
|
|
|
MD5Final(keybuf, &md5c);
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, keybuf, 16);
|
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, key->iv, 8);
|
2015-04-28 21:49:55 +03:00
|
|
|
MD5Final(keybuf+16, &md5c);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now decrypt the key blob.
|
2015-04-27 22:48:29 +03:00
|
|
|
*/
|
2015-04-28 21:49:55 +03:00
|
|
|
if (key->encryption == OP_E_3DES)
|
2018-05-26 10:31:34 +03:00
|
|
|
des3_decrypt_pubkey_ossh(keybuf, key->iv,
|
2018-05-28 19:42:03 +03:00
|
|
|
key->keyblob->u, key->keyblob->len);
|
2015-04-28 21:49:55 +03:00
|
|
|
else {
|
2018-09-13 16:43:04 +03:00
|
|
|
AESContext *ctx;
|
2015-04-28 21:49:55 +03:00
|
|
|
assert(key->encryption == OP_E_AES);
|
|
|
|
ctx = aes_make_context();
|
|
|
|
aes128_key(ctx, keybuf);
|
2018-05-26 10:31:34 +03:00
|
|
|
aes_iv(ctx, key->iv);
|
2018-05-28 19:42:03 +03:00
|
|
|
aes_ssh2_decrypt_blk(ctx, key->keyblob->u, key->keyblob->len);
|
2015-04-28 21:49:55 +03:00
|
|
|
aes_free_context(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
smemclr(&md5c, sizeof(md5c));
|
|
|
|
smemclr(keybuf, sizeof(keybuf));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we have a decrypted key blob, which contains an ASN.1
|
|
|
|
* encoded private key. We must now untangle the ASN.1.
|
|
|
|
*
|
|
|
|
* We expect the whole key blob to be formatted as a SEQUENCE
|
|
|
|
* (0x30 followed by a length code indicating that the rest of
|
|
|
|
* the blob is part of the sequence). Within that SEQUENCE we
|
|
|
|
* expect to see a bunch of INTEGERs. What those integers mean
|
|
|
|
* depends on the key type:
|
|
|
|
*
|
|
|
|
* - For RSA, we expect the integers to be 0, n, e, d, p, q,
|
|
|
|
* dmp1, dmq1, iqmp in that order. (The last three are d mod
|
|
|
|
* (p-1), d mod (q-1), inverse of q mod p respectively.)
|
|
|
|
*
|
|
|
|
* - For DSA, we expect them to be 0, p, q, g, y, x in that
|
|
|
|
* order.
|
|
|
|
*
|
|
|
|
* - In ECDSA the format is totally different: we see the
|
|
|
|
* SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv
|
|
|
|
* EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint
|
|
|
|
*/
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, key->keyblob->u, key->keyblob->len);
|
|
|
|
|
|
|
|
{
|
|
|
|
/* Expect the SEQUENCE header. Take its absence as a failure to
|
|
|
|
* decrypt, if the key was encrypted. */
|
|
|
|
ber_item seq = get_ber(src);
|
|
|
|
if (get_err(src) || seq.id != 16) {
|
|
|
|
errmsg = "ASN.1 decoding failure";
|
|
|
|
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reinitialise our BinarySource to parse just the inside of that
|
|
|
|
* SEQUENCE. */
|
|
|
|
BinarySource_BARE_INIT(src, seq.data.ptr, seq.data.len);
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
/* Expect a load of INTEGERs. */
|
|
|
|
if (key->keytype == OP_RSA)
|
|
|
|
num_integers = 9;
|
|
|
|
else if (key->keytype == OP_DSA)
|
|
|
|
num_integers = 6;
|
|
|
|
else
|
|
|
|
num_integers = 0; /* placate compiler warnings */
|
|
|
|
|
|
|
|
|
|
|
|
if (key->keytype == OP_ECDSA) {
|
|
|
|
/* And now for something completely different */
|
2018-05-28 19:42:03 +03:00
|
|
|
ber_item integer, privkey, sub0, sub1, oid, pubkey;
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 10:32:21 +03:00
|
|
|
const ssh_keyalg *alg;
|
2015-05-15 12:13:05 +03:00
|
|
|
const struct ec_curve *curve;
|
2018-05-28 19:42:03 +03:00
|
|
|
|
|
|
|
/* Parse the outer layer of things inside the containing SEQUENCE */
|
|
|
|
integer = get_ber(src);
|
|
|
|
privkey = get_ber(src);
|
|
|
|
sub0 = get_ber(src);
|
|
|
|
sub1 = get_ber(src);
|
|
|
|
|
|
|
|
/* Now look inside sub0 for the curve OID */
|
|
|
|
BinarySource_BARE_INIT(src, sub0.data.ptr, sub0.data.len);
|
|
|
|
oid = get_ber(src);
|
|
|
|
|
|
|
|
/* And inside sub1 for the public-key BIT STRING */
|
|
|
|
BinarySource_BARE_INIT(src, sub1.data.ptr, sub1.data.len);
|
|
|
|
pubkey = get_ber(src);
|
|
|
|
|
|
|
|
if (get_err(src) ||
|
|
|
|
integer.id != 2 ||
|
|
|
|
integer.data.len != 1 ||
|
|
|
|
((const unsigned char *)integer.data.ptr)[0] != 1 ||
|
|
|
|
privkey.id != 4 ||
|
|
|
|
sub0.id != 0 ||
|
|
|
|
sub1.id != 1 ||
|
|
|
|
oid.id != 6 ||
|
|
|
|
pubkey.id != 3) {
|
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "ASN.1 decoding failure";
|
|
|
|
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
|
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
|
|
|
|
alg = ec_alg_by_oid(oid.data.len, oid.data.ptr, &curve);
|
2015-05-15 12:13:05 +03:00
|
|
|
if (!alg) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "Unsupported ECDSA curve.";
|
|
|
|
retval = NULL;
|
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
if (pubkey.data.len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "ASN.1 decoding failure";
|
|
|
|
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
|
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
/* Skip 0x00 before point */
|
|
|
|
pubkey.data.ptr = (const char *)pubkey.data.ptr + 1;
|
|
|
|
pubkey.data.len -= 1;
|
2014-11-01 12:45:20 +03:00
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
/* Construct the key */
|
|
|
|
retkey = snew(struct ssh2_userkey);
|
2015-05-15 12:13:05 +03:00
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
put_stringz(blob, alg->ssh_id);
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(blob, curve->name);
|
2018-05-28 19:42:03 +03:00
|
|
|
put_stringpl(blob, pubkey.data);
|
2018-05-24 15:11:56 +03:00
|
|
|
publen = blob->len;
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, privkey.data.ptr, privkey.data.len);
|
2015-05-15 12:13:05 +03:00
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
retkey->key = ssh_key_new_priv(
|
|
|
|
alg, make_ptrlen(blob->u, publen),
|
Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
2018-05-31 20:40:51 +03:00
|
|
|
make_ptrlen(blob->u + publen, blob->len - publen));
|
2015-05-15 12:13:05 +03:00
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
if (!retkey->key) {
|
2015-04-28 21:49:55 +03:00
|
|
|
sfree(retkey);
|
|
|
|
errmsg = "unable to create key data structure";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (key->keytype == OP_RSA || key->keytype == OP_DSA) {
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa");
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
for (i = 0; i < num_integers; i++) {
|
2018-05-28 19:42:03 +03:00
|
|
|
ber_item integer = get_ber(src);
|
|
|
|
|
|
|
|
if (get_err(src) || integer.id != 2) {
|
2015-04-27 22:48:29 +03:00
|
|
|
errmsg = "ASN.1 decoding failure";
|
|
|
|
retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
|
|
|
|
goto error;
|
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
/*
|
|
|
|
* The first integer should be zero always (I think
|
|
|
|
* this is some sort of version indication).
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
if (integer.data.len != 1 ||
|
|
|
|
((const unsigned char *)integer.data.ptr)[0] != 0) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "version number mismatch";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else if (key->keytype == OP_RSA) {
|
|
|
|
/*
|
|
|
|
* Integers 1 and 2 go into the public blob but in the
|
|
|
|
* opposite order; integers 3, 4, 5 and 8 go into the
|
|
|
|
* private blob. The other two (6 and 7) are ignored.
|
|
|
|
*/
|
|
|
|
if (i == 1) {
|
|
|
|
/* Save the details for after we deal with number 2. */
|
2018-05-28 19:42:03 +03:00
|
|
|
modptr = integer.data.ptr;
|
|
|
|
modlen = integer.data.len;
|
2015-04-28 21:49:55 +03:00
|
|
|
} else if (i != 6 && i != 7) {
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, integer.data.ptr,
|
|
|
|
integer.data.len);
|
2015-04-28 21:49:55 +03:00
|
|
|
if (i == 2) {
|
2018-05-24 15:11:56 +03:00
|
|
|
put_mp_ssh2_from_string(blob, modptr, modlen);
|
|
|
|
privptr = blob->len;
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (key->keytype == OP_DSA) {
|
|
|
|
/*
|
|
|
|
* Integers 1-4 go into the public blob; integer 5 goes
|
|
|
|
* into the private blob.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, integer.data.ptr,
|
|
|
|
integer.data.len);
|
2015-04-28 21:49:55 +03:00
|
|
|
if (i == 4)
|
2018-05-24 15:11:56 +03:00
|
|
|
privptr = blob->len;
|
2015-04-27 22:48:29 +03:00
|
|
|
}
|
|
|
|
}
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2014-11-01 12:14:19 +03:00
|
|
|
/*
|
2015-04-28 21:49:55 +03:00
|
|
|
* Now put together the actual key. Simplest way to do this is
|
|
|
|
* to assemble our own key blobs and feed them to the createkey
|
|
|
|
* functions; this is a bit faffy but it does mean we get all
|
|
|
|
* the sanity checks for free.
|
2014-11-01 12:14:19 +03:00
|
|
|
*/
|
2015-04-28 21:49:55 +03:00
|
|
|
assert(privptr > 0); /* should have bombed by now if not */
|
|
|
|
retkey = snew(struct ssh2_userkey);
|
2018-06-03 14:58:05 +03:00
|
|
|
alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
|
|
|
|
retkey->key = ssh_key_new_priv(
|
|
|
|
alg, make_ptrlen(blob->u, privptr),
|
Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
2018-05-31 20:40:51 +03:00
|
|
|
make_ptrlen(blob->u+privptr, blob->len-privptr));
|
2018-05-24 15:11:56 +03:00
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
if (!retkey->key) {
|
2015-04-28 21:49:55 +03:00
|
|
|
sfree(retkey);
|
|
|
|
errmsg = "unable to create key data structure";
|
2014-11-01 12:14:19 +03:00
|
|
|
goto error;
|
|
|
|
}
|
2002-05-11 20:45:29 +04:00
|
|
|
|
2014-11-01 12:14:19 +03:00
|
|
|
} else {
|
2015-04-28 21:49:55 +03:00
|
|
|
assert(0 && "Bad key type from load_openssh_pem_key");
|
2015-08-11 11:44:00 +03:00
|
|
|
errmsg = "Bad key type from load_openssh_pem_key";
|
|
|
|
goto error;
|
2002-05-11 20:45:29 +04:00
|
|
|
}
|
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
/*
|
|
|
|
* The old key format doesn't include a comment in the private
|
|
|
|
* key file.
|
|
|
|
*/
|
|
|
|
retkey->comment = dupstr("imported-openssh-key");
|
|
|
|
|
2002-05-13 20:37:11 +04:00
|
|
|
errmsg = NULL; /* no error */
|
|
|
|
retval = retkey;
|
2002-05-11 20:45:29 +04:00
|
|
|
|
|
|
|
error:
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf_free(blob);
|
2018-05-28 19:42:03 +03:00
|
|
|
strbuf_free(key->keyblob);
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(key, sizeof(*key));
|
2002-05-11 20:45:29 +04:00
|
|
|
sfree(key);
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
2002-05-11 20:45:29 +04:00
|
|
|
return retval;
|
|
|
|
}
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_pem_write(
|
|
|
|
const Filename *filename, struct ssh2_userkey *key, const char *passphrase)
|
2002-05-14 22:11:15 +04:00
|
|
|
{
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *pubblob, *privblob, *outblob;
|
2018-05-24 12:59:39 +03:00
|
|
|
unsigned char *spareblob;
|
|
|
|
int sparelen = 0;
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen numbers[9];
|
2018-05-24 15:11:56 +03:00
|
|
|
int nnumbers, i;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *header, *footer;
|
2002-05-14 22:11:15 +04:00
|
|
|
char zero[1];
|
|
|
|
unsigned char iv[8];
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool ret = false;
|
2002-05-14 22:11:15 +04:00
|
|
|
FILE *fp;
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
2002-05-14 22:11:15 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the key blobs.
|
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
pubblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
|
2018-05-24 12:59:39 +03:00
|
|
|
privblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
|
2018-05-24 15:11:56 +03:00
|
|
|
spareblob = NULL;
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
outblob = strbuf_new();
|
2014-11-01 12:14:19 +03:00
|
|
|
|
2002-05-14 22:11:15 +04:00
|
|
|
/*
|
2014-11-01 12:14:19 +03:00
|
|
|
* Encode the OpenSSH key blob, and also decide on the header
|
|
|
|
* line.
|
2002-05-14 22:11:15 +04:00
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
if (ssh_key_alg(key->key) == &ssh_rsa ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_dss) {
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *seq;
|
|
|
|
|
2013-07-14 14:45:54 +04:00
|
|
|
/*
|
2014-11-01 12:14:19 +03:00
|
|
|
* The RSA and DSS handlers share some code because the two
|
|
|
|
* key types have very similar ASN.1 representations, as a
|
|
|
|
* plain SEQUENCE of big integers. So we set up a list of
|
|
|
|
* bignums per key type and then construct the actual blob in
|
|
|
|
* common code after that.
|
2013-07-14 14:45:54 +04:00
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
if (ssh_key_alg(key->key) == &ssh_rsa) {
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen n, e, d, p, q, iqmp, dmp1, dmq1;
|
2014-11-01 12:14:19 +03:00
|
|
|
Bignum bd, bp, bq, bdmp1, bdmq1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These blobs were generated from inside PuTTY, so we needn't
|
|
|
|
* treat them as untrusted.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
|
|
|
|
get_string(src); /* skip algorithm name */
|
|
|
|
e = get_string(src);
|
|
|
|
n = get_string(src);
|
|
|
|
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
|
|
|
|
d = get_string(src);
|
|
|
|
p = get_string(src);
|
|
|
|
q = get_string(src);
|
|
|
|
iqmp = get_string(src);
|
|
|
|
|
|
|
|
assert(!get_err(src)); /* can't go wrong */
|
2014-11-01 12:14:19 +03:00
|
|
|
|
|
|
|
/* We also need d mod (p-1) and d mod (q-1). */
|
2018-05-28 19:42:03 +03:00
|
|
|
bd = bignum_from_bytes(d.ptr, d.len);
|
|
|
|
bp = bignum_from_bytes(p.ptr, p.len);
|
|
|
|
bq = bignum_from_bytes(q.ptr, q.len);
|
2014-11-01 12:14:19 +03:00
|
|
|
decbn(bp);
|
|
|
|
decbn(bq);
|
|
|
|
bdmp1 = bigmod(bd, bp);
|
|
|
|
bdmq1 = bigmod(bd, bq);
|
|
|
|
freebn(bd);
|
|
|
|
freebn(bp);
|
|
|
|
freebn(bq);
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
dmp1.len = (bignum_bitcount(bdmp1)+8)/8;
|
|
|
|
dmq1.len = (bignum_bitcount(bdmq1)+8)/8;
|
|
|
|
sparelen = dmp1.len + dmq1.len;
|
2014-11-01 12:14:19 +03:00
|
|
|
spareblob = snewn(sparelen, unsigned char);
|
2018-05-28 19:42:03 +03:00
|
|
|
dmp1.ptr = spareblob;
|
|
|
|
dmq1.ptr = spareblob + dmp1.len;
|
|
|
|
for (i = 0; i < dmp1.len; i++)
|
|
|
|
spareblob[i] = bignum_byte(bdmp1, dmp1.len-1 - i);
|
|
|
|
for (i = 0; i < dmq1.len; i++)
|
|
|
|
spareblob[i+dmp1.len] = bignum_byte(bdmq1, dmq1.len-1 - i);
|
2014-11-01 12:14:19 +03:00
|
|
|
freebn(bdmp1);
|
|
|
|
freebn(bdmq1);
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
numbers[0] = make_ptrlen(zero, 1); zero[0] = '\0';
|
2014-11-01 12:14:19 +03:00
|
|
|
numbers[1] = n;
|
|
|
|
numbers[2] = e;
|
|
|
|
numbers[3] = d;
|
|
|
|
numbers[4] = p;
|
|
|
|
numbers[5] = q;
|
|
|
|
numbers[6] = dmp1;
|
|
|
|
numbers[7] = dmq1;
|
|
|
|
numbers[8] = iqmp;
|
|
|
|
|
|
|
|
nnumbers = 9;
|
|
|
|
header = "-----BEGIN RSA PRIVATE KEY-----\n";
|
|
|
|
footer = "-----END RSA PRIVATE KEY-----\n";
|
|
|
|
} else { /* ssh-dss */
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen p, q, g, y, x;
|
2014-11-01 12:14:19 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* These blobs were generated from inside PuTTY, so we needn't
|
|
|
|
* treat them as untrusted.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
|
|
|
|
get_string(src); /* skip algorithm name */
|
|
|
|
p = get_string(src);
|
|
|
|
q = get_string(src);
|
|
|
|
g = get_string(src);
|
|
|
|
y = get_string(src);
|
|
|
|
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
|
|
|
|
x = get_string(src);
|
|
|
|
|
|
|
|
assert(!get_err(src)); /* can't go wrong */
|
|
|
|
|
|
|
|
numbers[0].ptr = zero; numbers[0].len = 1; zero[0] = '\0';
|
2014-11-01 12:14:19 +03:00
|
|
|
numbers[1] = p;
|
|
|
|
numbers[2] = q;
|
|
|
|
numbers[3] = g;
|
|
|
|
numbers[4] = y;
|
|
|
|
numbers[5] = x;
|
|
|
|
|
|
|
|
nnumbers = 6;
|
|
|
|
header = "-----BEGIN DSA PRIVATE KEY-----\n";
|
|
|
|
footer = "-----END DSA PRIVATE KEY-----\n";
|
|
|
|
}
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
seq = strbuf_new();
|
2014-11-01 12:14:19 +03:00
|
|
|
for (i = 0; i < nnumbers; i++) {
|
2018-05-28 19:42:03 +03:00
|
|
|
put_ber_id_len(seq, 2, numbers[i].len, 0);
|
|
|
|
put_data(seq, numbers[i].ptr, numbers[i].len);
|
2014-11-01 12:14:19 +03:00
|
|
|
}
|
2018-05-24 15:11:56 +03:00
|
|
|
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
|
|
|
|
put_data(outblob, seq->s, seq->len);
|
|
|
|
strbuf_free(seq);
|
2018-06-03 14:58:05 +03:00
|
|
|
} else if (ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_ecdsa_nistp521) {
|
2015-05-15 12:13:05 +03:00
|
|
|
const unsigned char *oid;
|
2018-10-06 01:49:08 +03:00
|
|
|
struct ec_key *ec = container_of(key->key, struct ec_key, sshk);
|
2014-11-01 12:45:20 +03:00
|
|
|
int oidlen;
|
|
|
|
int pointlen;
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *seq, *sub;
|
2014-11-01 12:45:20 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Structure of asn1:
|
|
|
|
* SEQUENCE
|
|
|
|
* INTEGER 1
|
|
|
|
* OCTET STRING (private key)
|
|
|
|
* [0]
|
|
|
|
* OID (curve)
|
|
|
|
* [1]
|
|
|
|
* BIT STRING (0x00 public key point)
|
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
oid = ec_alg_oid(ssh_key_alg(key->key), &oidlen);
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 10:32:21 +03:00
|
|
|
pointlen = (ec->publicKey.curve->fieldBits + 7) / 8 * 2;
|
2014-11-01 12:45:20 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
seq = strbuf_new();
|
2014-11-01 12:45:20 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
/* INTEGER 1 */
|
|
|
|
put_ber_id_len(seq, 2, 1, 0);
|
|
|
|
put_byte(seq, 1);
|
|
|
|
|
|
|
|
/* OCTET STRING private key */
|
|
|
|
put_ber_id_len(seq, 4, privblob->len - 4, 0);
|
|
|
|
put_data(seq, privblob->s + 4, privblob->len - 4);
|
|
|
|
|
|
|
|
/* Subsidiary OID */
|
|
|
|
sub = strbuf_new();
|
|
|
|
put_ber_id_len(sub, 6, oidlen, 0);
|
|
|
|
put_data(sub, oid, oidlen);
|
|
|
|
|
|
|
|
/* Append the OID to the sequence */
|
|
|
|
put_ber_id_len(seq, 0, sub->len,
|
|
|
|
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
|
|
|
|
put_data(seq, sub->s, sub->len);
|
|
|
|
strbuf_free(sub);
|
|
|
|
|
|
|
|
/* Subsidiary BIT STRING */
|
|
|
|
sub = strbuf_new();
|
|
|
|
put_ber_id_len(sub, 3, 2 + pointlen, 0);
|
|
|
|
put_byte(sub, 0);
|
|
|
|
put_data(sub, pubblob->s+39, 1 + pointlen);
|
|
|
|
|
|
|
|
/* Append the BIT STRING to the sequence */
|
|
|
|
put_ber_id_len(seq, 1, sub->len,
|
|
|
|
ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED);
|
|
|
|
put_data(seq, sub->s, sub->len);
|
|
|
|
strbuf_free(sub);
|
|
|
|
|
|
|
|
/* Write the full sequence with header to the output blob. */
|
|
|
|
put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED);
|
|
|
|
put_data(outblob, seq->s, seq->len);
|
|
|
|
strbuf_free(seq);
|
2014-11-01 12:45:20 +03:00
|
|
|
|
|
|
|
header = "-----BEGIN EC PRIVATE KEY-----\n";
|
|
|
|
footer = "-----END EC PRIVATE KEY-----\n";
|
2002-05-14 22:11:15 +04:00
|
|
|
} else {
|
|
|
|
assert(0); /* zoinks! */
|
2005-04-24 18:43:00 +04:00
|
|
|
exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
|
2002-05-14 22:11:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encrypt the key.
|
2010-04-12 15:02:06 +04:00
|
|
|
*
|
|
|
|
* For the moment, we still encrypt our OpenSSH keys using
|
|
|
|
* old-style 3DES.
|
2002-05-14 22:11:15 +04:00
|
|
|
*/
|
|
|
|
if (passphrase) {
|
2014-11-01 12:14:19 +03:00
|
|
|
struct MD5Context md5c;
|
|
|
|
unsigned char keybuf[32];
|
2018-05-24 15:11:56 +03:00
|
|
|
int origlen, outlen, pad, i;
|
2014-11-01 12:14:19 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Padding on OpenSSH keys is deterministic. The number of
|
|
|
|
* padding bytes is always more than zero, and always at most
|
|
|
|
* the cipher block length. The value of each padding byte is
|
|
|
|
* equal to the number of padding bytes. So a plaintext that's
|
|
|
|
* an exact multiple of the block size will be padded with 08
|
|
|
|
* 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
|
|
|
|
* plaintext one byte less than a multiple of the block size
|
|
|
|
* will be padded with just 01.
|
|
|
|
*
|
|
|
|
* This enables the OpenSSL key decryption function to strip
|
|
|
|
* off the padding algorithmically and return the unpadded
|
|
|
|
* plaintext to the next layer: it looks at the final byte, and
|
|
|
|
* then expects to find that many bytes at the end of the data
|
|
|
|
* with the same value. Those are all removed and the rest is
|
|
|
|
* returned.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
origlen = outblob->len;
|
|
|
|
outlen = (origlen + 8) &~ 7;
|
|
|
|
pad = outlen - origlen;
|
2018-06-09 11:01:07 +03:00
|
|
|
put_padding(outblob, pad, pad);
|
2014-11-01 12:14:19 +03:00
|
|
|
|
2002-05-14 22:11:15 +04:00
|
|
|
/*
|
|
|
|
* Invent an iv. Then derive encryption key from passphrase
|
|
|
|
* and iv/salt:
|
|
|
|
*
|
|
|
|
* - let block A equal MD5(passphrase || iv)
|
|
|
|
* - let block B equal MD5(A || passphrase || iv)
|
|
|
|
* - block C would be MD5(B || passphrase || iv) and so on
|
|
|
|
* - encryption key is the first N bytes of A || B
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 8; i++) iv[i] = random_byte();
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, iv, 8);
|
2002-05-14 22:11:15 +04:00
|
|
|
MD5Final(keybuf, &md5c);
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, keybuf, 16);
|
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, iv, 8);
|
2002-05-14 22:11:15 +04:00
|
|
|
MD5Final(keybuf+16, &md5c);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now encrypt the key blob.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
des3_encrypt_pubkey_ossh(keybuf, iv,
|
|
|
|
outblob->u, outlen);
|
2002-05-14 22:11:15 +04:00
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(&md5c, sizeof(md5c));
|
|
|
|
smemclr(keybuf, sizeof(keybuf));
|
2002-05-14 22:11:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And save it. We'll use Unix line endings just in case it's
|
|
|
|
* subsequently transferred in binary mode.
|
|
|
|
*/
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "wb", true); /* ensure Unix line endings */
|
2002-05-14 22:11:15 +04:00
|
|
|
if (!fp)
|
|
|
|
goto error;
|
|
|
|
fputs(header, fp);
|
|
|
|
if (passphrase) {
|
|
|
|
fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,");
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
fprintf(fp, "%02X", iv[i]);
|
|
|
|
fprintf(fp, "\n\n");
|
|
|
|
}
|
2018-05-24 15:11:56 +03:00
|
|
|
base64_encode(fp, outblob->u, outblob->len, 64);
|
2002-05-14 22:11:15 +04:00
|
|
|
fputs(footer, fp);
|
|
|
|
fclose(fp);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
ret = true;
|
2002-05-14 22:11:15 +04:00
|
|
|
|
|
|
|
error:
|
2018-05-24 15:11:56 +03:00
|
|
|
if (outblob)
|
|
|
|
strbuf_free(outblob);
|
2002-05-14 22:11:15 +04:00
|
|
|
if (spareblob) {
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(spareblob, sparelen);
|
2002-05-14 22:11:15 +04:00
|
|
|
sfree(spareblob);
|
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
if (privblob)
|
|
|
|
strbuf_free(privblob);
|
|
|
|
if (pubblob)
|
|
|
|
strbuf_free(pubblob);
|
2002-05-14 22:11:15 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-04-28 21:49:55 +03:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* Code to read and write OpenSSH private keys in the new-style format.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef enum {
|
2018-04-09 10:28:43 +03:00
|
|
|
ON_E_NONE, ON_E_AES256CBC, ON_E_AES256CTR
|
2015-04-28 21:49:55 +03:00
|
|
|
} openssh_new_cipher;
|
|
|
|
typedef enum {
|
|
|
|
ON_K_NONE, ON_K_BCRYPT
|
|
|
|
} openssh_new_kdf;
|
|
|
|
|
|
|
|
struct openssh_new_key {
|
|
|
|
openssh_new_cipher cipher;
|
|
|
|
openssh_new_kdf kdf;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
int rounds;
|
|
|
|
/* This points to a position within keyblob, not a
|
|
|
|
* separately allocated thing */
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen salt;
|
2015-04-28 21:49:55 +03:00
|
|
|
} bcrypt;
|
|
|
|
} kdfopts;
|
|
|
|
int nkeys, key_wanted;
|
|
|
|
/* This too points to a position within keyblob */
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen private;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
unsigned char *keyblob;
|
|
|
|
int keyblob_len, keyblob_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
|
|
|
|
const char **errmsg_p)
|
|
|
|
{
|
|
|
|
struct openssh_new_key *ret;
|
|
|
|
FILE *fp = NULL;
|
|
|
|
char *line = NULL;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
|
|
|
char *p;
|
2015-04-28 21:49:55 +03:00
|
|
|
char base64_bit[4];
|
|
|
|
int base64_chars = 0;
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
|
|
|
ptrlen str;
|
|
|
|
unsigned key_index;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
ret = snew(struct openssh_new_key);
|
|
|
|
ret->keyblob = NULL;
|
|
|
|
ret->keyblob_len = ret->keyblob_size = 0;
|
|
|
|
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "r", false);
|
2015-04-28 21:49:55 +03:00
|
|
|
if (!fp) {
|
|
|
|
errmsg = "unable to open key file";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(line = fgetline(fp))) {
|
|
|
|
errmsg = "unexpected end of file";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
strip_crlf(line);
|
|
|
|
if (0 != strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) {
|
|
|
|
errmsg = "file does not begin with OpenSSH new-style key header";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
smemclr(line, strlen(line));
|
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (!(line = fgetline(fp))) {
|
|
|
|
errmsg = "unexpected end of file";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
strip_crlf(line);
|
|
|
|
if (0 == strcmp(line, "-----END OPENSSH PRIVATE KEY-----")) {
|
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
break; /* done */
|
|
|
|
}
|
|
|
|
|
|
|
|
p = line;
|
|
|
|
while (isbase64(*p)) {
|
|
|
|
base64_bit[base64_chars++] = *p;
|
|
|
|
if (base64_chars == 4) {
|
|
|
|
unsigned char out[3];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
base64_chars = 0;
|
|
|
|
|
|
|
|
len = base64_decode_atom(base64_bit, out);
|
|
|
|
|
|
|
|
if (len <= 0) {
|
|
|
|
errmsg = "invalid base64 encoding";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret->keyblob_len + len > ret->keyblob_size) {
|
|
|
|
ret->keyblob_size = ret->keyblob_len + len + 256;
|
|
|
|
ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
|
|
|
|
unsigned char);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(ret->keyblob + ret->keyblob_len, out, len);
|
|
|
|
ret->keyblob_len += len;
|
|
|
|
|
|
|
|
smemclr(out, sizeof(out));
|
|
|
|
}
|
|
|
|
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
smemclr(line, strlen(line));
|
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
fp = NULL;
|
|
|
|
|
|
|
|
if (ret->keyblob_len == 0 || !ret->keyblob) {
|
|
|
|
errmsg = "key body not present";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, ret->keyblob, ret->keyblob_len);
|
2015-04-28 21:49:55 +03:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (strcmp(get_asciz(src), "openssh-key-v1") != 0) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "new-style OpenSSH magic number missing\n";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
/* Cipher name */
|
|
|
|
str = get_string(src);
|
|
|
|
if (ptrlen_eq_string(str, "none")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->cipher = ON_E_NONE;
|
2018-05-28 19:42:03 +03:00
|
|
|
} else if (ptrlen_eq_string(str, "aes256-cbc")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->cipher = ON_E_AES256CBC;
|
2018-05-28 19:42:03 +03:00
|
|
|
} else if (ptrlen_eq_string(str, "aes256-ctr")) {
|
2018-04-09 10:28:43 +03:00
|
|
|
ret->cipher = ON_E_AES256CTR;
|
2015-04-28 21:49:55 +03:00
|
|
|
} else {
|
2018-05-28 19:42:03 +03:00
|
|
|
errmsg = get_err(src) ? "no cipher name found" :
|
|
|
|
"unrecognised cipher name\n";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
/* Key derivation function name */
|
|
|
|
str = get_string(src);
|
|
|
|
if (ptrlen_eq_string(str, "none")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->kdf = ON_K_NONE;
|
2018-05-28 19:42:03 +03:00
|
|
|
} else if (ptrlen_eq_string(str, "bcrypt")) {
|
2015-04-28 21:49:55 +03:00
|
|
|
ret->kdf = ON_K_BCRYPT;
|
|
|
|
} else {
|
2018-05-28 19:42:03 +03:00
|
|
|
errmsg = get_err(src) ? "no kdf name found" :
|
|
|
|
"unrecognised kdf name\n";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
/* KDF extra options */
|
|
|
|
str = get_string(src);
|
2015-04-28 21:49:55 +03:00
|
|
|
switch (ret->kdf) {
|
|
|
|
case ON_K_NONE:
|
2018-05-28 19:42:03 +03:00
|
|
|
if (str.len != 0) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "expected empty options string for 'none' kdf";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ON_K_BCRYPT:
|
2018-05-28 19:42:03 +03:00
|
|
|
{
|
|
|
|
BinarySource opts[1];
|
|
|
|
|
|
|
|
BinarySource_BARE_INIT(opts, str.ptr, str.len);
|
|
|
|
ret->kdfopts.bcrypt.salt = get_string(opts);
|
|
|
|
ret->kdfopts.bcrypt.rounds = get_uint32(opts);
|
|
|
|
|
|
|
|
if (get_err(opts)) {
|
|
|
|
errmsg = "failed to parse bcrypt options string";
|
|
|
|
goto error;
|
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we expect a uint32 saying how many keys are
|
|
|
|
* stored in this file. OpenSSH new-style key files can
|
|
|
|
* contain more than one. Currently we don't have any user
|
|
|
|
* interface to specify which one we're trying to extract, so
|
|
|
|
* we just bomb out with an error if more than one is found in
|
|
|
|
* the file. However, I've put in all the mechanism here to
|
|
|
|
* extract the nth one for a given n, in case we later connect
|
|
|
|
* up some UI to that mechanism. Just arrange that the
|
|
|
|
* 'key_wanted' field is set to a value in the range [0,
|
|
|
|
* nkeys) by some mechanism.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
ret->nkeys = toint(get_uint32(src));
|
|
|
|
if (ret->nkeys != 1) {
|
|
|
|
errmsg = get_err(src) ? "no key count found" :
|
|
|
|
"multiple keys in new-style OpenSSH key file not supported\n";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
ret->key_wanted = 0;
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
/* Read and ignore a string per public key. */
|
|
|
|
for (key_index = 0; key_index < ret->nkeys; key_index++)
|
|
|
|
str = get_string(src);
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we expect a string containing the encrypted part of the
|
|
|
|
* key file.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
ret->private = get_string(src);
|
|
|
|
if (get_err(src)) {
|
|
|
|
errmsg = "no private key container string found\n";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And now we're done, until asked to actually decrypt.
|
|
|
|
*/
|
|
|
|
|
|
|
|
smemclr(base64_bit, sizeof(base64_bit));
|
|
|
|
if (errmsg_p) *errmsg_p = NULL;
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (line) {
|
|
|
|
smemclr(line, strlen(line));
|
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
|
|
|
smemclr(base64_bit, sizeof(base64_bit));
|
|
|
|
if (ret) {
|
|
|
|
if (ret->keyblob) {
|
|
|
|
smemclr(ret->keyblob, ret->keyblob_size);
|
|
|
|
sfree(ret->keyblob);
|
|
|
|
}
|
|
|
|
smemclr(ret, sizeof(*ret));
|
|
|
|
sfree(ret);
|
|
|
|
}
|
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
|
|
|
if (fp) fclose(fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_new_encrypted(const Filename *filename)
|
2015-04-28 21:49:55 +03:00
|
|
|
{
|
|
|
|
struct openssh_new_key *key = load_openssh_new_key(filename, NULL);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool ret;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
if (!key)
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
return false;
|
2015-04-28 21:49:55 +03:00
|
|
|
ret = (key->cipher != ON_E_NONE);
|
|
|
|
smemclr(key->keyblob, key->keyblob_size);
|
|
|
|
sfree(key->keyblob);
|
|
|
|
smemclr(key, sizeof(*key));
|
|
|
|
sfree(key);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static struct ssh2_userkey *openssh_new_read(
|
|
|
|
const Filename *filename, const char *passphrase, const char **errmsg_p)
|
2015-04-28 21:49:55 +03:00
|
|
|
{
|
|
|
|
struct openssh_new_key *key = load_openssh_new_key(filename, errmsg_p);
|
2017-02-15 00:31:12 +03:00
|
|
|
struct ssh2_userkey *retkey = NULL;
|
2015-04-28 21:49:55 +03:00
|
|
|
struct ssh2_userkey *retval = NULL;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
2018-05-28 19:42:03 +03:00
|
|
|
unsigned checkint;
|
|
|
|
BinarySource src[1];
|
|
|
|
int key_index;
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 10:32:21 +03:00
|
|
|
const ssh_keyalg *alg = NULL;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (key->cipher != ON_E_NONE) {
|
|
|
|
unsigned char keybuf[48];
|
|
|
|
int keysize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct the decryption key, and decrypt the string.
|
|
|
|
*/
|
|
|
|
switch (key->cipher) {
|
|
|
|
case ON_E_NONE:
|
|
|
|
keysize = 0;
|
|
|
|
break;
|
|
|
|
case ON_E_AES256CBC:
|
2018-04-09 10:28:43 +03:00
|
|
|
case ON_E_AES256CTR:
|
2015-04-28 21:49:55 +03:00
|
|
|
keysize = 48; /* 32 byte key + 16 byte IV */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0 && "Bad cipher enumeration value");
|
|
|
|
}
|
|
|
|
assert(keysize <= sizeof(keybuf));
|
|
|
|
switch (key->kdf) {
|
|
|
|
case ON_K_NONE:
|
|
|
|
memset(keybuf, 0, keysize);
|
|
|
|
break;
|
|
|
|
case ON_K_BCRYPT:
|
|
|
|
openssh_bcrypt(passphrase,
|
2018-05-28 19:42:03 +03:00
|
|
|
key->kdfopts.bcrypt.salt.ptr,
|
|
|
|
key->kdfopts.bcrypt.salt.len,
|
2015-04-28 21:49:55 +03:00
|
|
|
key->kdfopts.bcrypt.rounds,
|
|
|
|
keybuf, keysize);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0 && "Bad kdf enumeration value");
|
|
|
|
}
|
|
|
|
switch (key->cipher) {
|
|
|
|
case ON_E_NONE:
|
|
|
|
break;
|
|
|
|
case ON_E_AES256CBC:
|
2018-04-09 10:28:43 +03:00
|
|
|
case ON_E_AES256CTR:
|
2018-05-28 19:42:03 +03:00
|
|
|
if (key->private.len % 16 != 0) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "private key container length is not a"
|
|
|
|
" multiple of AES block size\n";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
void *ctx = aes_make_context();
|
|
|
|
aes256_key(ctx, keybuf);
|
|
|
|
aes_iv(ctx, keybuf + 32);
|
2018-05-28 19:42:03 +03:00
|
|
|
/* Decrypt the private section in place, casting away
|
|
|
|
* the const from key->private being a ptrlen */
|
2018-04-09 10:28:43 +03:00
|
|
|
if (key->cipher == ON_E_AES256CBC) {
|
2018-05-28 19:42:03 +03:00
|
|
|
aes_ssh2_decrypt_blk(ctx, (char *)key->private.ptr,
|
|
|
|
key->private.len);
|
2018-04-09 10:28:43 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-05-28 19:42:03 +03:00
|
|
|
aes_ssh2_sdctr(ctx, (char *)key->private.ptr,
|
|
|
|
key->private.len);
|
2018-04-09 10:28:43 +03:00
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
aes_free_context(ctx);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0 && "Bad cipher enumeration value");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now parse the entire encrypted section, and extract the key
|
|
|
|
* identified by key_wanted.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, key->private.ptr, key->private.len);
|
2015-04-28 21:49:55 +03:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
checkint = get_uint32(src);
|
|
|
|
if (get_uint32(src) != checkint || get_err(src)) {
|
2015-04-28 21:49:55 +03:00
|
|
|
errmsg = "decryption check failed";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
retkey = snew(struct ssh2_userkey);
|
|
|
|
retkey->key = NULL;
|
|
|
|
retkey->comment = NULL;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
2018-06-03 14:58:05 +03:00
|
|
|
for (key_index = 0; key_index < key->nkeys; key_index++) {
|
|
|
|
ptrlen comment;
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
/*
|
2018-06-03 14:58:05 +03:00
|
|
|
* Identify the key type.
|
2015-04-28 21:49:55 +03:00
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
alg = find_pubkey_alg_len(get_string(src));
|
2015-05-02 17:11:41 +03:00
|
|
|
if (!alg) {
|
|
|
|
errmsg = "private key type not recognised\n";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-06-03 14:58:05 +03:00
|
|
|
* Read the key. We have to do this even if it's not the one
|
|
|
|
* we want, because it's the only way to find out how much
|
|
|
|
* data to skip past to get to the next key in the file.
|
2015-04-28 21:49:55 +03:00
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
retkey->key = ssh_key_new_priv_openssh(alg, src);
|
2018-05-28 19:42:03 +03:00
|
|
|
if (get_err(src)) {
|
|
|
|
errmsg = "unable to read entire private key";
|
|
|
|
goto error;
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
2018-06-03 14:58:05 +03:00
|
|
|
if (!retkey->key) {
|
|
|
|
errmsg = "unable to create key data structure";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (key_index != key->key_wanted) {
|
|
|
|
/*
|
|
|
|
* If this isn't the key we're looking for, throw it away.
|
|
|
|
*/
|
|
|
|
ssh_key_free(retkey->key);
|
|
|
|
retkey->key = NULL;
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the key comment.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
comment = get_string(src);
|
|
|
|
if (get_err(src)) {
|
|
|
|
errmsg = "unable to read key comment";
|
2015-04-28 21:49:55 +03:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (key_index == key->key_wanted) {
|
|
|
|
assert(retkey);
|
2018-05-28 19:42:03 +03:00
|
|
|
retkey->comment = mkstr(comment);
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retkey) {
|
|
|
|
errmsg = "key index out of range";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we expect nothing left but padding.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
{
|
|
|
|
unsigned char expected_pad_byte = 1;
|
|
|
|
while (get_avail(src) > 0)
|
|
|
|
if (get_byte(src) != expected_pad_byte++) {
|
|
|
|
errmsg = "padding at end of private string did not match";
|
|
|
|
goto error;
|
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
errmsg = NULL; /* no error */
|
|
|
|
retval = retkey;
|
2017-02-15 00:31:12 +03:00
|
|
|
retkey = NULL; /* prevent the free */
|
2015-04-28 21:49:55 +03:00
|
|
|
|
|
|
|
error:
|
2017-02-15 00:31:12 +03:00
|
|
|
if (retkey) {
|
|
|
|
sfree(retkey->comment);
|
2018-06-03 14:58:05 +03:00
|
|
|
if (retkey->key)
|
|
|
|
ssh_key_free(retkey->key);
|
2017-02-15 00:31:12 +03:00
|
|
|
sfree(retkey);
|
|
|
|
}
|
2015-04-28 21:49:55 +03:00
|
|
|
smemclr(key->keyblob, key->keyblob_size);
|
|
|
|
sfree(key->keyblob);
|
|
|
|
smemclr(key, sizeof(*key));
|
|
|
|
sfree(key);
|
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_new_write(
|
|
|
|
const Filename *filename, struct ssh2_userkey *key, const char *passphrase)
|
2015-04-28 21:46:58 +03:00
|
|
|
{
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *pubblob, *privblob, *cblob;
|
|
|
|
int padvalue, i;
|
2015-04-28 21:51:52 +03:00
|
|
|
unsigned checkint;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool ret = false;
|
2015-04-28 21:51:52 +03:00
|
|
|
unsigned char bcrypt_salt[16];
|
|
|
|
const int bcrypt_rounds = 16;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the key blobs and find out the lengths of things.
|
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
pubblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
|
2018-05-24 12:59:39 +03:00
|
|
|
privblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_openssh_blob(key->key, BinarySink_UPCAST(privblob));
|
2015-04-28 21:51:52 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct the cleartext version of the blob.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
cblob = strbuf_new();
|
2015-04-28 21:51:52 +03:00
|
|
|
|
|
|
|
/* Magic number. */
|
2018-05-24 15:11:56 +03:00
|
|
|
put_asciz(cblob, "openssh-key-v1");
|
2015-04-28 21:51:52 +03:00
|
|
|
|
|
|
|
/* Cipher and kdf names, and kdf options. */
|
|
|
|
if (!passphrase) {
|
|
|
|
memset(bcrypt_salt, 0, sizeof(bcrypt_salt)); /* prevent warnings */
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(cblob, "none");
|
|
|
|
put_stringz(cblob, "none");
|
|
|
|
put_stringz(cblob, "");
|
2015-04-28 21:51:52 +03:00
|
|
|
} else {
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *substr;
|
|
|
|
|
2015-04-28 21:51:52 +03:00
|
|
|
for (i = 0; i < (int)sizeof(bcrypt_salt); i++)
|
|
|
|
bcrypt_salt[i] = random_byte();
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(cblob, "aes256-ctr");
|
|
|
|
put_stringz(cblob, "bcrypt");
|
|
|
|
substr = strbuf_new();
|
|
|
|
put_string(substr, bcrypt_salt, sizeof(bcrypt_salt));
|
|
|
|
put_uint32(substr, bcrypt_rounds);
|
|
|
|
put_stringsb(cblob, substr);
|
2015-04-28 21:51:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Number of keys. */
|
2018-05-24 15:11:56 +03:00
|
|
|
put_uint32(cblob, 1);
|
2015-04-28 21:51:52 +03:00
|
|
|
|
|
|
|
/* Public blob. */
|
2018-05-24 15:11:56 +03:00
|
|
|
put_string(cblob, pubblob->s, pubblob->len);
|
2015-04-28 21:51:52 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
/* Private section. */
|
|
|
|
{
|
|
|
|
strbuf *cpblob = strbuf_new();
|
|
|
|
|
|
|
|
/* checkint. */
|
|
|
|
checkint = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
checkint = (checkint << 8) + random_byte();
|
|
|
|
put_uint32(cpblob, checkint);
|
|
|
|
put_uint32(cpblob, checkint);
|
|
|
|
|
|
|
|
/* Private key. The main private blob goes inline, with no string
|
|
|
|
* wrapper. */
|
2018-06-03 14:58:05 +03:00
|
|
|
put_stringz(cpblob, ssh_key_ssh_id(key->key));
|
2018-05-24 15:11:56 +03:00
|
|
|
put_data(cpblob, privblob->s, privblob->len);
|
|
|
|
|
|
|
|
/* Comment. */
|
|
|
|
put_stringz(cpblob, key->comment);
|
|
|
|
|
|
|
|
/* Pad out the encrypted section. */
|
|
|
|
padvalue = 1;
|
|
|
|
do {
|
|
|
|
put_byte(cpblob, padvalue++);
|
|
|
|
} while (cpblob->len & 15);
|
|
|
|
|
|
|
|
if (passphrase) {
|
|
|
|
/*
|
|
|
|
* Encrypt the private section. We need 48 bytes of key
|
|
|
|
* material: 32 bytes AES key + 16 bytes iv.
|
|
|
|
*/
|
|
|
|
unsigned char keybuf[48];
|
|
|
|
void *ctx;
|
2015-04-28 21:51:52 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
openssh_bcrypt(passphrase,
|
|
|
|
bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
|
|
|
|
keybuf, sizeof(keybuf));
|
2015-04-28 21:51:52 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
ctx = aes_make_context();
|
|
|
|
aes256_key(ctx, keybuf);
|
|
|
|
aes_iv(ctx, keybuf + 32);
|
|
|
|
aes_ssh2_sdctr(ctx, cpblob->u,
|
|
|
|
cpblob->len);
|
|
|
|
aes_free_context(ctx);
|
2015-04-28 21:51:52 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
smemclr(keybuf, sizeof(keybuf));
|
|
|
|
}
|
2015-04-28 21:51:52 +03:00
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringsb(cblob, cpblob);
|
2015-04-28 21:51:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And save it. We'll use Unix line endings just in case it's
|
|
|
|
* subsequently transferred in binary mode.
|
|
|
|
*/
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "wb", true); /* ensure Unix line endings */
|
2015-04-28 21:51:52 +03:00
|
|
|
if (!fp)
|
|
|
|
goto error;
|
|
|
|
fputs("-----BEGIN OPENSSH PRIVATE KEY-----\n", fp);
|
2018-05-24 15:11:56 +03:00
|
|
|
base64_encode(fp, cblob->u, cblob->len, 64);
|
2015-04-28 21:51:52 +03:00
|
|
|
fputs("-----END OPENSSH PRIVATE KEY-----\n", fp);
|
|
|
|
fclose(fp);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
ret = true;
|
2015-04-28 21:51:52 +03:00
|
|
|
|
|
|
|
error:
|
2018-05-24 15:11:56 +03:00
|
|
|
if (cblob)
|
|
|
|
strbuf_free(cblob);
|
2018-05-24 12:59:39 +03:00
|
|
|
if (privblob)
|
|
|
|
strbuf_free(privblob);
|
|
|
|
if (pubblob)
|
|
|
|
strbuf_free(pubblob);
|
2015-04-28 21:51:52 +03:00
|
|
|
return ret;
|
2015-04-28 21:46:58 +03:00
|
|
|
}
|
|
|
|
|
2015-05-10 09:42:48 +03:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* The switch function openssh_auto_write(), which chooses one of the
|
|
|
|
* concrete OpenSSH output formats based on the key type.
|
|
|
|
*/
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool openssh_auto_write(
|
|
|
|
const Filename *filename, struct ssh2_userkey *key, const char *passphrase)
|
2015-05-10 09:42:48 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The old OpenSSH format supports a fixed list of key types. We
|
|
|
|
* assume that anything not in that fixed list is newer, and hence
|
|
|
|
* will use the new format.
|
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
if (ssh_key_alg(key->key) == &ssh_dss ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_rsa ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
|
|
|
|
ssh_key_alg(key->key) == &ssh_ecdsa_nistp521)
|
2015-05-10 09:42:48 +03:00
|
|
|
return openssh_pem_write(filename, key, passphrase);
|
|
|
|
else
|
|
|
|
return openssh_new_write(filename, key, passphrase);
|
|
|
|
}
|
|
|
|
|
2002-05-13 20:32:42 +04:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* Code to read ssh.com private keys.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2005-03-10 19:36:05 +03:00
|
|
|
* The format of the base64 blob is largely SSH-2-packet-formatted,
|
2002-05-13 20:32:42 +04:00
|
|
|
* except that mpints are a bit different: they're more like the
|
2005-03-10 19:36:05 +03:00
|
|
|
* old SSH-1 mpint. You have a 32-bit bit count N, followed by
|
2002-05-13 20:32:42 +04:00
|
|
|
* (N+7)/8 bytes of data.
|
|
|
|
*
|
|
|
|
* So. The blob contains:
|
|
|
|
*
|
|
|
|
* - uint32 0x3f6ff9eb (magic number)
|
|
|
|
* - uint32 size (total blob size)
|
|
|
|
* - string key-type (see below)
|
|
|
|
* - string cipher-type (tells you if key is encrypted)
|
|
|
|
* - string encrypted-blob
|
|
|
|
*
|
|
|
|
* (The first size field includes the size field itself and the
|
2005-03-10 19:36:05 +03:00
|
|
|
* magic number before it. All other size fields are ordinary SSH-2
|
2002-05-13 20:32:42 +04:00
|
|
|
* strings, so the size field indicates how much data is to
|
|
|
|
* _follow_.)
|
|
|
|
*
|
|
|
|
* The encrypted blob, once decrypted, contains a single string
|
|
|
|
* which in turn contains the payload. (This allows padding to be
|
|
|
|
* added after that string while still making it clear where the
|
|
|
|
* real payload ends. Also it probably makes for a reasonable
|
|
|
|
* decryption check.)
|
|
|
|
*
|
|
|
|
* The payload blob, for an RSA key, contains:
|
|
|
|
* - mpint e
|
|
|
|
* - mpint d
|
|
|
|
* - mpint n (yes, the public and private stuff is intermixed)
|
|
|
|
* - mpint u (presumably inverse of p mod q)
|
|
|
|
* - mpint p (p is the smaller prime)
|
|
|
|
* - mpint q (q is the larger)
|
|
|
|
*
|
|
|
|
* For a DSA key, the payload blob contains:
|
|
|
|
* - uint32 0
|
|
|
|
* - mpint p
|
|
|
|
* - mpint g
|
|
|
|
* - mpint q
|
|
|
|
* - mpint y
|
|
|
|
* - mpint x
|
|
|
|
*
|
|
|
|
* Alternatively, if the parameters are `predefined', that
|
|
|
|
* (0,p,g,q) sequence can be replaced by a uint32 1 and a string
|
|
|
|
* containing some predefined parameter specification. *shudder*,
|
|
|
|
* but I doubt we'll encounter this in real life.
|
|
|
|
*
|
|
|
|
* The key type strings are ghastly. The RSA key I looked at had a
|
|
|
|
* type string of
|
|
|
|
*
|
|
|
|
* `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}'
|
|
|
|
*
|
|
|
|
* and the DSA key wasn't much better:
|
|
|
|
*
|
|
|
|
* `dl-modp{sign{dsa-nist-sha1},dh{plain}}'
|
|
|
|
*
|
|
|
|
* It isn't clear that these will always be the same. I think it
|
|
|
|
* might be wise just to look at the `if-modn{sign{rsa' and
|
|
|
|
* `dl-modp{sign{dsa' prefixes.
|
|
|
|
*
|
|
|
|
* Finally, the encryption. The cipher-type string appears to be
|
2005-03-10 19:36:05 +03:00
|
|
|
* either `none' or `3des-cbc'. Looks as if this is SSH-2-style
|
2002-05-13 20:32:42 +04:00
|
|
|
* 3des-cbc (i.e. outer cbc rather than inner). The key is created
|
|
|
|
* from the passphrase by means of yet another hashing faff:
|
|
|
|
*
|
|
|
|
* - first 16 bytes are MD5(passphrase)
|
|
|
|
* - next 16 bytes are MD5(passphrase || first 16 bytes)
|
|
|
|
* - if there were more, they'd be MD5(passphrase || first 32),
|
|
|
|
* and so on.
|
|
|
|
*/
|
|
|
|
|
2002-05-15 23:16:45 +04:00
|
|
|
#define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb
|
|
|
|
|
2002-05-13 20:32:42 +04:00
|
|
|
struct sshcom_key {
|
|
|
|
char comment[256]; /* allowing any length is overkill */
|
|
|
|
unsigned char *keyblob;
|
|
|
|
int keyblob_len, keyblob_size;
|
|
|
|
};
|
|
|
|
|
2005-02-28 02:01:11 +03:00
|
|
|
static struct sshcom_key *load_sshcom_key(const Filename *filename,
|
|
|
|
const char **errmsg_p)
|
2002-05-13 20:32:42 +04:00
|
|
|
{
|
|
|
|
struct sshcom_key *ret;
|
|
|
|
FILE *fp;
|
2007-01-07 17:20:28 +03:00
|
|
|
char *line = NULL;
|
|
|
|
int hdrstart, len;
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
|
|
|
char *p;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool headers_done;
|
2002-05-13 20:32:42 +04:00
|
|
|
char base64_bit[4];
|
|
|
|
int base64_chars = 0;
|
|
|
|
|
2003-03-29 19:14:26 +03:00
|
|
|
ret = snew(struct sshcom_key);
|
2002-05-13 20:32:42 +04:00
|
|
|
ret->comment[0] = '\0';
|
|
|
|
ret->keyblob = NULL;
|
|
|
|
ret->keyblob_len = ret->keyblob_size = 0;
|
|
|
|
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "r", false);
|
2002-05-13 20:32:42 +04:00
|
|
|
if (!fp) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unable to open key file";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
if (!(line = fgetline(fp))) {
|
|
|
|
errmsg = "unexpected end of file";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
strip_crlf(line);
|
|
|
|
if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "file does not begin with ssh.com key header";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
headers_done = false;
|
2002-05-13 20:32:42 +04:00
|
|
|
while (1) {
|
2007-01-07 17:20:28 +03:00
|
|
|
if (!(line = fgetline(fp))) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unexpected end of file";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
strip_crlf(line);
|
2013-07-14 14:46:07 +04:00
|
|
|
if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) {
|
|
|
|
sfree(line);
|
2013-07-20 17:15:11 +04:00
|
|
|
line = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
break; /* done */
|
2013-07-14 14:46:07 +04:00
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
if ((p = strchr(line, ':')) != NULL) {
|
2002-05-13 20:32:42 +04:00
|
|
|
if (headers_done) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "header found in body of key data";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*p++ = '\0';
|
|
|
|
while (*p && isspace((unsigned char)*p)) p++;
|
2007-01-07 17:20:28 +03:00
|
|
|
hdrstart = p - line;
|
|
|
|
|
2002-05-13 20:32:42 +04:00
|
|
|
/*
|
|
|
|
* Header lines can end in a trailing backslash for
|
|
|
|
* continuation.
|
|
|
|
*/
|
2007-01-07 17:20:28 +03:00
|
|
|
len = hdrstart + strlen(line+hdrstart);
|
|
|
|
assert(!line[len]);
|
|
|
|
while (line[len-1] == '\\') {
|
|
|
|
char *line2;
|
|
|
|
int line2len;
|
|
|
|
|
|
|
|
line2 = fgetline(fp);
|
|
|
|
if (!line2) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "unexpected end of file";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
strip_crlf(line2);
|
|
|
|
|
|
|
|
line2len = strlen(line2);
|
|
|
|
line = sresize(line, len + line2len + 1, char);
|
|
|
|
strcpy(line + len - 1, line2);
|
|
|
|
len += line2len - 1;
|
|
|
|
assert(!line[len]);
|
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line2, strlen(line2));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line2);
|
|
|
|
line2 = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
}
|
2007-01-07 17:20:28 +03:00
|
|
|
p = line + hdrstart;
|
|
|
|
strip_crlf(p);
|
|
|
|
if (!strcmp(line, "Comment")) {
|
2002-05-13 20:32:42 +04:00
|
|
|
/* Strip quotes in comment if present. */
|
|
|
|
if (p[0] == '"' && p[strlen(p)-1] == '"') {
|
|
|
|
p++;
|
|
|
|
p[strlen(p)-1] = '\0';
|
|
|
|
}
|
|
|
|
strncpy(ret->comment, p, sizeof(ret->comment));
|
|
|
|
ret->comment[sizeof(ret->comment)-1] = '\0';
|
|
|
|
}
|
|
|
|
} else {
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
headers_done = true;
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2007-01-07 17:20:28 +03:00
|
|
|
p = line;
|
2002-05-13 20:32:42 +04:00
|
|
|
while (isbase64(*p)) {
|
|
|
|
base64_bit[base64_chars++] = *p;
|
|
|
|
if (base64_chars == 4) {
|
|
|
|
unsigned char out[3];
|
|
|
|
|
|
|
|
base64_chars = 0;
|
|
|
|
|
|
|
|
len = base64_decode_atom(base64_bit, out);
|
|
|
|
|
|
|
|
if (len <= 0) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "invalid base64 encoding";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret->keyblob_len + len > ret->keyblob_size) {
|
|
|
|
ret->keyblob_size = ret->keyblob_len + len + 256;
|
2003-03-29 19:14:26 +03:00
|
|
|
ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
|
|
|
|
unsigned char);
|
2002-05-13 20:32:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(ret->keyblob + ret->keyblob_len, out, len);
|
|
|
|
ret->keyblob_len += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret->keyblob_len == 0 || !ret->keyblob) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "key body not present";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2013-07-14 14:46:07 +04:00
|
|
|
fclose(fp);
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
2013-07-14 14:46:07 +04:00
|
|
|
if (fp)
|
|
|
|
fclose(fp);
|
|
|
|
|
2007-01-07 17:20:28 +03:00
|
|
|
if (line) {
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(line, strlen(line));
|
2007-01-07 17:20:28 +03:00
|
|
|
sfree(line);
|
|
|
|
line = NULL;
|
|
|
|
}
|
2002-05-13 20:32:42 +04:00
|
|
|
if (ret) {
|
2002-05-13 20:37:11 +04:00
|
|
|
if (ret->keyblob) {
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(ret->keyblob, ret->keyblob_size);
|
2002-05-13 20:37:11 +04:00
|
|
|
sfree(ret->keyblob);
|
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(ret, sizeof(*ret));
|
2002-05-13 20:32:42 +04:00
|
|
|
sfree(ret);
|
|
|
|
}
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
2002-05-13 20:32:42 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool sshcom_encrypted(const Filename *filename, char **comment)
|
2002-05-13 20:32:42 +04:00
|
|
|
{
|
2005-02-28 02:01:11 +03:00
|
|
|
struct sshcom_key *key = load_sshcom_key(filename, NULL);
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
|
|
|
ptrlen str;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool answer = false;
|
2013-07-14 14:46:07 +04:00
|
|
|
|
2002-05-13 20:32:42 +04:00
|
|
|
*comment = NULL;
|
|
|
|
if (!key)
|
2013-07-14 14:46:07 +04:00
|
|
|
goto done;
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (get_uint32(src) != SSHCOM_MAGIC_NUMBER)
|
|
|
|
goto done; /* key is invalid */
|
|
|
|
get_uint32(src); /* skip length field */
|
|
|
|
get_string(src); /* skip key type */
|
|
|
|
str = get_string(src); /* cipher type */
|
|
|
|
if (get_err(src))
|
|
|
|
goto done; /* key is invalid */
|
|
|
|
if (!ptrlen_eq_string(str, "none"))
|
2018-10-29 22:50:29 +03:00
|
|
|
answer = true;
|
2002-05-13 20:32:42 +04:00
|
|
|
|
|
|
|
done:
|
2013-07-20 17:15:20 +04:00
|
|
|
if (key) {
|
|
|
|
*comment = dupstr(key->comment);
|
|
|
|
smemclr(key->keyblob, key->keyblob_size);
|
|
|
|
sfree(key->keyblob);
|
|
|
|
smemclr(key, sizeof(*key));
|
|
|
|
sfree(key);
|
|
|
|
} else {
|
|
|
|
*comment = dupstr("");
|
|
|
|
}
|
2002-05-13 20:32:42 +04:00
|
|
|
return answer;
|
|
|
|
}
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
void BinarySink_put_mp_sshcom_from_string(
|
|
|
|
BinarySink *bs, const void *bytesv, int nbytes)
|
2002-05-15 23:16:45 +04:00
|
|
|
{
|
2018-05-24 15:11:56 +03:00
|
|
|
const unsigned char *bytes = (const unsigned char *)bytesv;
|
|
|
|
int bits = nbytes * 8 - 1;
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
while (bits > 0) {
|
2018-05-24 15:11:56 +03:00
|
|
|
if (*bytes & (1 << (bits & 7)))
|
2002-05-15 23:16:45 +04:00
|
|
|
break;
|
|
|
|
if (!(bits-- & 7))
|
2018-05-24 15:11:56 +03:00
|
|
|
bytes++, nbytes--;
|
2002-05-15 23:16:45 +04:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
put_uint32(bs, bits+1);
|
|
|
|
put_data(bs, bytes, nbytes);
|
2002-05-15 23:16:45 +04:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
#define put_mp_sshcom_from_string(bs, val, len) \
|
|
|
|
BinarySink_put_mp_sshcom_from_string(BinarySink_UPCAST(bs), val, len)
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
static ptrlen BinarySource_get_mp_sshcom_as_string(BinarySource *src)
|
|
|
|
{
|
|
|
|
unsigned bits = get_uint32(src);
|
|
|
|
return get_data(src, (bits + 7) / 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define get_mp_sshcom_as_string(bs) \
|
|
|
|
BinarySource_get_mp_sshcom_as_string(BinarySource_UPCAST(bs))
|
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static struct ssh2_userkey *sshcom_read(
|
|
|
|
const Filename *filename, const char *passphrase, const char **errmsg_p)
|
2002-05-13 20:32:42 +04:00
|
|
|
{
|
2005-02-28 02:01:11 +03:00
|
|
|
struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *errmsg;
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
|
|
|
ptrlen str, ciphertext;
|
|
|
|
int publen;
|
2002-05-13 20:32:42 +04:00
|
|
|
const char prefix_rsa[] = "if-modn{sign{rsa";
|
|
|
|
const char prefix_dsa[] = "dl-modp{sign{dsa";
|
|
|
|
enum { RSA, DSA } type;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool encrypted;
|
2002-05-13 20:32:42 +04:00
|
|
|
struct ssh2_userkey *ret = NULL, *retkey;
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 10:32:21 +03:00
|
|
|
const ssh_keyalg *alg;
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *blob = NULL;
|
2002-05-13 20:32:42 +04:00
|
|
|
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
|
|
|
|
|
|
|
|
if (get_uint32(src) != SSHCOM_MAGIC_NUMBER) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "key does not begin with magic number";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
get_uint32(src); /* skip length field */
|
2002-05-13 20:32:42 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the key type.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
str = get_string(src);
|
|
|
|
if (str.len > sizeof(prefix_rsa) - 1 &&
|
|
|
|
!memcmp(str.ptr, prefix_rsa, sizeof(prefix_rsa) - 1)) {
|
2002-05-13 20:32:42 +04:00
|
|
|
type = RSA;
|
2018-05-28 19:42:03 +03:00
|
|
|
} else if (str.len > sizeof(prefix_dsa) - 1 &&
|
|
|
|
!memcmp(str.ptr, prefix_dsa, sizeof(prefix_dsa) - 1)) {
|
2002-05-13 20:32:42 +04:00
|
|
|
type = DSA;
|
|
|
|
} else {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "key is of unknown type";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the cipher type.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
str = get_string(src);
|
|
|
|
if (ptrlen_eq_string(str, "none"))
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
encrypted = false;
|
2018-05-28 19:42:03 +03:00
|
|
|
else if (ptrlen_eq_string(str, "3des-cbc"))
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
encrypted = true;
|
2002-05-13 20:32:42 +04:00
|
|
|
else {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "key encryption is of unknown type";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get hold of the encrypted part of the key.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
ciphertext = get_string(src);
|
|
|
|
if (ciphertext.len == 0) {
|
|
|
|
errmsg = "no key data found";
|
2002-05-13 20:32:42 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decrypt it if necessary.
|
|
|
|
*/
|
|
|
|
if (encrypted) {
|
|
|
|
/*
|
|
|
|
* Derive encryption key from passphrase and iv/salt:
|
|
|
|
*
|
|
|
|
* - let block A equal MD5(passphrase)
|
|
|
|
* - let block B equal MD5(passphrase || A)
|
|
|
|
* - block C would be MD5(passphrase || A || B) and so on
|
|
|
|
* - encryption key is the first N bytes of A || B
|
|
|
|
*/
|
|
|
|
struct MD5Context md5c;
|
|
|
|
unsigned char keybuf[32], iv[8];
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (ciphertext.len % 8 != 0) {
|
2005-02-28 02:01:11 +03:00
|
|
|
errmsg = "encrypted part of key is not a multiple of cipher block"
|
2002-05-13 20:32:42 +04:00
|
|
|
" size";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
2002-05-13 20:32:42 +04:00
|
|
|
MD5Final(keybuf, &md5c);
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, keybuf, 16);
|
2002-05-13 20:32:42 +04:00
|
|
|
MD5Final(keybuf+16, &md5c);
|
|
|
|
|
|
|
|
/*
|
2018-05-28 19:42:03 +03:00
|
|
|
* Now decrypt the key blob in place (casting away const from
|
|
|
|
* ciphertext being a ptrlen).
|
2002-05-13 20:32:42 +04:00
|
|
|
*/
|
2002-05-13 20:37:11 +04:00
|
|
|
memset(iv, 0, sizeof(iv));
|
2018-05-28 19:42:03 +03:00
|
|
|
des3_decrypt_pubkey_ossh(keybuf, iv,
|
|
|
|
(char *)ciphertext.ptr, ciphertext.len);
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(&md5c, sizeof(md5c));
|
|
|
|
smemclr(keybuf, sizeof(keybuf));
|
2002-05-13 20:37:11 +04:00
|
|
|
|
2002-05-13 20:32:42 +04:00
|
|
|
/*
|
|
|
|
* Hereafter we return WRONG_PASSPHRASE for any parsing
|
2002-05-13 20:37:11 +04:00
|
|
|
* error. (But only if we've just tried to decrypt it!
|
|
|
|
* Returning WRONG_PASSPHRASE for an unencrypted key is
|
|
|
|
* automatic doom.)
|
2002-05-13 20:32:42 +04:00
|
|
|
*/
|
|
|
|
if (encrypted)
|
|
|
|
ret = SSH2_WRONG_PASSPHRASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-05-28 19:42:03 +03:00
|
|
|
* Expect the ciphertext to be formatted as a containing string,
|
|
|
|
* and reinitialise src to start parsing the inside of that string.
|
2002-05-13 20:32:42 +04:00
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, ciphertext.ptr, ciphertext.len);
|
|
|
|
str = get_string(src);
|
|
|
|
if (get_err(src)) {
|
2002-05-13 20:32:42 +04:00
|
|
|
errmsg = "containing string was ill-formed";
|
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, str.ptr, str.len);
|
2002-05-13 20:32:42 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we break down into RSA versus DSA. In either case we'll
|
|
|
|
* construct public and private blobs in our own format, and
|
2018-06-03 14:58:05 +03:00
|
|
|
* end up feeding them to ssh_key_new_priv().
|
2002-05-13 20:32:42 +04:00
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
blob = strbuf_new();
|
2002-05-13 20:32:42 +04:00
|
|
|
if (type == RSA) {
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen n, e, d, u, p, q;
|
|
|
|
|
|
|
|
e = get_mp_sshcom_as_string(src);
|
|
|
|
d = get_mp_sshcom_as_string(src);
|
|
|
|
n = get_mp_sshcom_as_string(src);
|
|
|
|
u = get_mp_sshcom_as_string(src);
|
|
|
|
p = get_mp_sshcom_as_string(src);
|
|
|
|
q = get_mp_sshcom_as_string(src);
|
|
|
|
if (get_err(src)) {
|
2002-05-13 20:32:42 +04:00
|
|
|
errmsg = "key data did not contain six integers";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
alg = &ssh_rsa;
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(blob, "ssh-rsa");
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, e.ptr, e.len);
|
|
|
|
put_mp_ssh2_from_string(blob, n.ptr, n.len);
|
2018-05-24 15:11:56 +03:00
|
|
|
publen = blob->len;
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, d.ptr, d.len);
|
|
|
|
put_mp_ssh2_from_string(blob, q.ptr, q.len);
|
|
|
|
put_mp_ssh2_from_string(blob, p.ptr, p.len);
|
|
|
|
put_mp_ssh2_from_string(blob, u.ptr, u.len);
|
2013-07-14 14:46:39 +04:00
|
|
|
} else {
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen p, q, g, x, y;
|
2013-07-15 10:40:59 +04:00
|
|
|
|
|
|
|
assert(type == DSA); /* the only other option from the if above */
|
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
if (get_uint32(src) != 0) {
|
2002-05-13 20:32:42 +04:00
|
|
|
errmsg = "predefined DSA parameters not supported";
|
|
|
|
goto error;
|
|
|
|
}
|
2018-05-28 19:42:03 +03:00
|
|
|
p = get_mp_sshcom_as_string(src);
|
|
|
|
g = get_mp_sshcom_as_string(src);
|
|
|
|
q = get_mp_sshcom_as_string(src);
|
|
|
|
y = get_mp_sshcom_as_string(src);
|
|
|
|
x = get_mp_sshcom_as_string(src);
|
|
|
|
if (get_err(src)) {
|
2002-05-13 20:32:42 +04:00
|
|
|
errmsg = "key data did not contain five integers";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
alg = &ssh_dss;
|
2018-05-24 15:11:56 +03:00
|
|
|
put_stringz(blob, "ssh-dss");
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, p.ptr, p.len);
|
|
|
|
put_mp_ssh2_from_string(blob, q.ptr, q.len);
|
|
|
|
put_mp_ssh2_from_string(blob, g.ptr, g.len);
|
|
|
|
put_mp_ssh2_from_string(blob, y.ptr, y.len);
|
2018-05-24 15:11:56 +03:00
|
|
|
publen = blob->len;
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_ssh2_from_string(blob, x.ptr, x.len);
|
2013-07-14 14:46:39 +04:00
|
|
|
}
|
2002-05-13 20:32:42 +04:00
|
|
|
|
2003-03-29 19:14:26 +03:00
|
|
|
retkey = snew(struct ssh2_userkey);
|
2018-06-03 14:58:05 +03:00
|
|
|
retkey->key = ssh_key_new_priv(
|
Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
2018-05-31 20:40:51 +03:00
|
|
|
alg, make_ptrlen(blob->u, publen),
|
|
|
|
make_ptrlen(blob->u + publen, blob->len - publen));
|
2018-06-03 14:58:05 +03:00
|
|
|
if (!retkey->key) {
|
2002-05-13 20:32:42 +04:00
|
|
|
sfree(retkey);
|
|
|
|
errmsg = "unable to create key data structure";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
retkey->comment = dupstr(key->comment);
|
|
|
|
|
|
|
|
errmsg = NULL; /* no error */
|
|
|
|
ret = retkey;
|
|
|
|
|
|
|
|
error:
|
2002-05-13 20:37:11 +04:00
|
|
|
if (blob) {
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf_free(blob);
|
2002-05-13 20:37:11 +04:00
|
|
|
}
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(key->keyblob, key->keyblob_size);
|
2002-05-13 20:32:42 +04:00
|
|
|
sfree(key->keyblob);
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(key, sizeof(*key));
|
2002-05-13 20:32:42 +04:00
|
|
|
sfree(key);
|
2005-02-28 02:01:11 +03:00
|
|
|
if (errmsg_p) *errmsg_p = errmsg;
|
2002-05-13 20:32:42 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2002-05-15 23:16:45 +04:00
|
|
|
|
2018-11-03 11:25:28 +03:00
|
|
|
static bool sshcom_write(
|
|
|
|
const Filename *filename, struct ssh2_userkey *key, const char *passphrase)
|
2002-05-15 23:16:45 +04:00
|
|
|
{
|
2018-05-24 15:11:56 +03:00
|
|
|
strbuf *pubblob, *privblob, *outblob;
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen numbers[6];
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
int nnumbers, lenpos, i;
|
|
|
|
bool initial_zero;
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource src[1];
|
2015-05-15 13:15:42 +03:00
|
|
|
const char *type;
|
2002-05-15 23:16:45 +04:00
|
|
|
char *ciphertext;
|
|
|
|
int cipherlen;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
bool ret = false;
|
2002-05-15 23:16:45 +04:00
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the key blobs.
|
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
pubblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
|
2018-05-24 12:59:39 +03:00
|
|
|
privblob = strbuf_new();
|
2018-06-03 14:58:05 +03:00
|
|
|
ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
|
2002-05-15 23:16:45 +04:00
|
|
|
outblob = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the sequence of integers to be encoded into the OpenSSH
|
|
|
|
* key blob, and also decide on the header line.
|
|
|
|
*/
|
2018-06-03 14:58:05 +03:00
|
|
|
if (ssh_key_alg(key->key) == &ssh_rsa) {
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen n, e, d, p, q, iqmp;
|
2002-05-15 23:16:45 +04:00
|
|
|
|
2013-07-14 14:45:54 +04:00
|
|
|
/*
|
|
|
|
* These blobs were generated from inside PuTTY, so we needn't
|
|
|
|
* treat them as untrusted.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
|
|
|
|
get_string(src); /* skip algorithm name */
|
|
|
|
e = get_string(src);
|
|
|
|
n = get_string(src);
|
|
|
|
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
|
|
|
|
d = get_string(src);
|
|
|
|
p = get_string(src);
|
|
|
|
q = get_string(src);
|
|
|
|
iqmp = get_string(src);
|
|
|
|
|
|
|
|
assert(!get_err(src)); /* can't go wrong */
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
numbers[0] = e;
|
|
|
|
numbers[1] = d;
|
|
|
|
numbers[2] = n;
|
|
|
|
numbers[3] = iqmp;
|
|
|
|
numbers[4] = q;
|
|
|
|
numbers[5] = p;
|
|
|
|
|
|
|
|
nnumbers = 6;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
initial_zero = false;
|
2002-05-15 23:16:45 +04:00
|
|
|
type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
|
2018-06-03 14:58:05 +03:00
|
|
|
} else if (ssh_key_alg(key->key) == &ssh_dss) {
|
2018-05-28 19:42:03 +03:00
|
|
|
ptrlen p, q, g, y, x;
|
2002-05-15 23:16:45 +04:00
|
|
|
|
2013-07-14 14:45:54 +04:00
|
|
|
/*
|
|
|
|
* These blobs were generated from inside PuTTY, so we needn't
|
|
|
|
* treat them as untrusted.
|
|
|
|
*/
|
2018-05-28 19:42:03 +03:00
|
|
|
BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
|
|
|
|
get_string(src); /* skip algorithm name */
|
|
|
|
p = get_string(src);
|
|
|
|
q = get_string(src);
|
|
|
|
g = get_string(src);
|
|
|
|
y = get_string(src);
|
|
|
|
BinarySource_BARE_INIT(src, privblob->u, privblob->len);
|
|
|
|
x = get_string(src);
|
2002-05-15 23:16:45 +04:00
|
|
|
|
2018-05-28 19:42:03 +03:00
|
|
|
assert(!get_err(src)); /* can't go wrong */
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
numbers[0] = p;
|
|
|
|
numbers[1] = g;
|
|
|
|
numbers[2] = q;
|
|
|
|
numbers[3] = y;
|
|
|
|
numbers[4] = x;
|
|
|
|
|
|
|
|
nnumbers = 5;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
initial_zero = true;
|
2002-05-15 23:16:45 +04:00
|
|
|
type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
|
|
|
|
} else {
|
2018-06-03 17:38:06 +03:00
|
|
|
goto error; /* unsupported key type */
|
2002-05-15 23:16:45 +04:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:11:56 +03:00
|
|
|
outblob = strbuf_new();
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the unencrypted key blob.
|
|
|
|
*/
|
2018-05-24 15:11:56 +03:00
|
|
|
put_uint32(outblob, SSHCOM_MAGIC_NUMBER);
|
|
|
|
put_uint32(outblob, 0); /* length field, fill in later */
|
|
|
|
put_stringz(outblob, type);
|
|
|
|
put_stringz(outblob, passphrase ? "3des-cbc" : "none");
|
|
|
|
lenpos = outblob->len; /* remember this position */
|
|
|
|
put_uint32(outblob, 0); /* encrypted-blob size */
|
|
|
|
put_uint32(outblob, 0); /* encrypted-payload size */
|
|
|
|
if (initial_zero)
|
|
|
|
put_uint32(outblob, 0);
|
2002-05-15 23:16:45 +04:00
|
|
|
for (i = 0; i < nnumbers; i++)
|
2018-05-28 19:42:03 +03:00
|
|
|
put_mp_sshcom_from_string(outblob, numbers[i].ptr, numbers[i].len);
|
2002-05-15 23:16:45 +04:00
|
|
|
/* Now wrap up the encrypted payload. */
|
2018-05-24 15:11:56 +03:00
|
|
|
PUT_32BIT(outblob->s + lenpos + 4,
|
|
|
|
outblob->len - (lenpos + 8));
|
2002-05-15 23:16:45 +04:00
|
|
|
/* Pad encrypted blob to a multiple of cipher block size. */
|
|
|
|
if (passphrase) {
|
2018-05-24 15:11:56 +03:00
|
|
|
int padding = -(outblob->len - (lenpos+4)) & 7;
|
2002-05-15 23:16:45 +04:00
|
|
|
while (padding--)
|
2018-05-24 15:11:56 +03:00
|
|
|
put_byte(outblob, random_byte());
|
2002-05-15 23:16:45 +04:00
|
|
|
}
|
2018-05-24 15:11:56 +03:00
|
|
|
ciphertext = outblob->s + lenpos + 4;
|
|
|
|
cipherlen = outblob->len - (lenpos + 4);
|
2002-05-15 23:16:45 +04:00
|
|
|
assert(!passphrase || cipherlen % 8 == 0);
|
|
|
|
/* Wrap up the encrypted blob string. */
|
2018-05-24 15:11:56 +03:00
|
|
|
PUT_32BIT(outblob->s + lenpos, cipherlen);
|
2002-05-15 23:16:45 +04:00
|
|
|
/* And finally fill in the total length field. */
|
2018-05-24 15:11:56 +03:00
|
|
|
PUT_32BIT(outblob->s + 4, outblob->len);
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Encrypt the key.
|
|
|
|
*/
|
|
|
|
if (passphrase) {
|
|
|
|
/*
|
|
|
|
* Derive encryption key from passphrase and iv/salt:
|
|
|
|
*
|
|
|
|
* - let block A equal MD5(passphrase)
|
|
|
|
* - let block B equal MD5(passphrase || A)
|
|
|
|
* - block C would be MD5(passphrase || A || B) and so on
|
|
|
|
* - encryption key is the first N bytes of A || B
|
|
|
|
*/
|
|
|
|
struct MD5Context md5c;
|
|
|
|
unsigned char keybuf[32], iv[8];
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
2002-05-15 23:16:45 +04:00
|
|
|
MD5Final(keybuf, &md5c);
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, passphrase, strlen(passphrase));
|
|
|
|
put_data(&md5c, keybuf, 16);
|
2002-05-15 23:16:45 +04:00
|
|
|
MD5Final(keybuf+16, &md5c);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now decrypt the key blob.
|
|
|
|
*/
|
|
|
|
memset(iv, 0, sizeof(iv));
|
2018-05-26 10:31:34 +03:00
|
|
|
des3_encrypt_pubkey_ossh(keybuf, iv, ciphertext, cipherlen);
|
2002-05-15 23:16:45 +04:00
|
|
|
|
2012-07-22 23:51:50 +04:00
|
|
|
smemclr(&md5c, sizeof(md5c));
|
|
|
|
smemclr(keybuf, sizeof(keybuf));
|
2002-05-15 23:16:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And save it. We'll use Unix line endings just in case it's
|
|
|
|
* subsequently transferred in binary mode.
|
|
|
|
*/
|
2018-10-29 22:50:29 +03:00
|
|
|
fp = f_open(filename, "wb", true); /* ensure Unix line endings */
|
2002-05-15 23:16:45 +04:00
|
|
|
if (!fp)
|
|
|
|
goto error;
|
|
|
|
fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
|
|
|
|
fprintf(fp, "Comment: \"");
|
|
|
|
/*
|
|
|
|
* Comment header is broken with backslash-newline if it goes
|
|
|
|
* over 70 chars. Although it's surrounded by quotes, it
|
|
|
|
* _doesn't_ escape backslashes or quotes within the string.
|
|
|
|
* Don't ask me, I didn't design it.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int slen = 60; /* starts at 60 due to "Comment: " */
|
|
|
|
char *c = key->comment;
|
2002-10-25 17:00:45 +04:00
|
|
|
while ((int)strlen(c) > slen) {
|
2002-05-15 23:16:45 +04:00
|
|
|
fprintf(fp, "%.*s\\\n", slen, c);
|
|
|
|
c += slen;
|
|
|
|
slen = 70; /* allow 70 chars on subsequent lines */
|
|
|
|
}
|
|
|
|
fprintf(fp, "%s\"\n", c);
|
|
|
|
}
|
2018-05-24 15:11:56 +03:00
|
|
|
base64_encode(fp, outblob->u, outblob->len, 70);
|
2002-05-15 23:16:45 +04:00
|
|
|
fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
|
|
|
|
fclose(fp);
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 22:23:19 +03:00
|
|
|
ret = true;
|
2002-05-15 23:16:45 +04:00
|
|
|
|
|
|
|
error:
|
2018-05-24 15:11:56 +03:00
|
|
|
if (outblob)
|
|
|
|
strbuf_free(outblob);
|
2018-05-24 12:59:39 +03:00
|
|
|
if (privblob)
|
|
|
|
strbuf_free(privblob);
|
|
|
|
if (pubblob)
|
|
|
|
strbuf_free(pubblob);
|
2002-05-15 23:16:45 +04:00
|
|
|
return ret;
|
|
|
|
}
|