I'm trying to connect to a remote host to issue a command, but I'm getting the following error message while running the code:
ssh: handshake failed: ssh: no common algorithm for key exchange; client offered: [curve25519-sha256#libssh.org ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 diffie-hellman-group14-sha1], server offered: [diffie-hellman-group1-sha1]panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x759836]
Here is the code that I'm using:
func (SSHClient *SSH) Connect(mode int) {
var SSHConfig *ssh.ClientConfig
var auth []ssh.AuthMethod
if mode == CERT_PUBLIC_KEY_FILE {
auth = []ssh.AuthMethod{SSHClient.readPublicKeyFile(SSHClient.Cert)}
}
SSHConfig = &ssh.ClientConfig{
User: SSHClient.User,
Auth: auth,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: time.Second * DEFAULT_TIMEOUT,
}
SSHConfig.Config.Ciphers = append(SSHConfig.Config.Ciphers, "diffie-hellman-group1-sha1")
client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", SSHClient.IP, SSHClient.Port), SSHConfig)
if err != nil {
fmt.Printf("ERROR - While trying to Dial to the host %s with error: %s", SSHClient.IP, err.Error())
return
}
session, err := client.NewSession()
if err != nil {
fmt.Printf("ERROR - While trying to create a new session on host %s with error: %s", SSHClient.IP, err.Error())
client.Close()
return
}
SSHClient.session = session
SSHClient.client = client
}
Any ideas on how to resolve this issue?
Thanks in advance.
The problem is.... the server is only willing to talk over diffie-hellman-group1-sha1
And:
golang/go issue 2903: ssh: add diffie-hellman-group1-sha1, has been closed 6 days ago
golang/go/issue 17230: proposal: x/crypto/ssh: support Diffie-Hellman Group Exchange from RFC 4419, is being implemented now.
So you would need for your client a fork of golang.org/x/crypto/ssh, like bored-engineer/ssh, where commit 39a91b and commit fe5e4ff does add support for diffie-hellman-group1-sha1.
Or install the latest of golang/crypto, which includes commit 57b3e21.
The panic is somewhat strange. Clearly something goes wrong when no key exchange algorithm can be agreed-to. As VonC notes, Diffie-Hellman key exchange was only added fairly recently (June 3). Since your server offers only that algorithm, you can't get started without it.
This is not the cause of the panic (which seems to happen inside ssh.Dial itself), but I will note that when you do this:
SSHConfig.Config.Ciphers = append(SSHConfig.Config.Ciphers, "diffie-hellman-group1-sha1")
you wind up telling the Go code to only use diffie-hellman-group1-sha1 as the channel encryption. You do not add to anything here. The reason is that SSHConfig.Config.Ciphers is initially nil. So you might as well write:
SSHConfig.Config.Ciphers = []string{"diffie-hellman-group1-sha1"}
to get the same effect, which is: things won't work.
You can call SetDefaults so that the list is non-empty before adding to the list, but adding to the list is ineffective if there is no implementation for this mode—and even with the new commits, Diffie-Hellman isn't allowed for anything other than the key exchange itself. Note that ssh.Dial calls ssh.NewClientConn, which is here and which starts with:
fullConf := *config
fullConf.SetDefaults()
SetDefaults in turn is here and contains:
if c.Ciphers == nil {
c.Ciphers = preferredCiphers
}
var ciphers []string
for _, c := range c.Ciphers {
if cipherModes[c] != nil {
// reject the cipher if we have no cipherModes definition
ciphers = append(ciphers, c)
}
}
c.Ciphers = ciphers
which first says that if the config's Ciphers is not set, it should use the defaults, and then immediately after that, filters away any string that's not in cipherModes. This in turn is defined here and starts with this comment:
// cipherModes documents properties of supported ciphers. Ciphers not included
// are not supported and will not be negotiated, even if explicitly requested in
// ClientConfig.Crypto.Ciphers.
This phrase is not in the documentation. It should be! Ciphers not included are not supported and will not be negotiated, even if explicitly requested in ClientConfig.Crypto.Ciphers.
(See the last link above for the set of ciphers that are supported. Note that this list has grown over time.)
Diffie-hellman-group1-sha1 is a key exchange algorithm. Should be KeyExchanges instead of Ciphers in the Config struct
SSHConfig.Config.KeyExchanges = append(SSHConfig.Config.KeyExchanges, "diffie-hellman-group1-sha1")
Instead of
SSHConfig.Config.Ciphers = append(SSHConfig.Config.Ciphers, "diffie-hellman-group1-sha1")
If KeyExchanges is not specified, the default algorithms used can be found in ssh/common.go
// preferredKexAlgos specifies the default preference for key-exchange algorithms
// in preference order.
var preferredKexAlgos = []string{
kexAlgoCurve25519SHA256,
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA1,
}
As you can see, kexAlgoDH1SHA1 or diffie-hellman-group1-sha1 is not listed at this time
Related
I'm trying to upload a file to S3 and have it encrypted using the SSE-C encryption options. I can upload without the SSE-C options, but when I supply the sse_customer_key options I'm getting the following error:
ArgumentError: header x-amz-server-side-encryption-customer-key has field value "QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0\nnMUE2MTNENDRCOTcxRjA2Qzk1Mg=", this cannot include CR/LF
I'm not sure if the problem is with the key I'm generating or with the encoding. I've played around with different options here, but the AWS documentation is not very clear. In the general SSE-C documentation it says you need to supply a x-amz-server-side-encryption-customer-key header, which is described as this:
Use this header to provide the 256-bit, base64-encoded encryption key
for Amazon S3 to use to encrypt or decrypt your data.
However, if I look at the Ruby SDK documentation for uploading a file the 3 options have a slightly different description
:sse_customer_algorithm (String) — Specifies the algorithm to use to when encrypting the object (e.g.,
:sse_customer_key (String) — Specifies the customer-provided encryption key for Amazon S3 to use in
:sse_customer_key_md5 (String) — Specifies the 128-bit MD5 digest of the encryption key according to RFC
(I didn't copy that wrong, the AWS documentation is literally half-written like that)
So the SDK documentation makes it seem like you supply the raw sse_customer_key and that it would base64-encode it on your behalf (which makes sense to me).
So right now I'm building the options like this:
sse_customer_algorithm: :AES256,
sse_customer_key: sse_customer_key,
sse_customer_key_md5: Digest::MD5.hexdigest(sse_customer_key)
I previously tried doing Base64.encode64(sse_customer_key) but that gave me a different error:
Aws::S3::Errors::InvalidArgument: The secret key was invalid for the
specified algorithm
I'm not sure if I'm generating the key incorrectly or if I'm supplying the key incorrectly (or if it's a different problem altogether).
This is how I'm generating the key:
require "openssl"
OpenSSL::Cipher.new("AES-256-CBC").random_key
Oh, did you notice that your key contains '\n'? That's most probably why you get the CR/LF error:
QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0(\n)nMUE2MTNENDRCOTcxRjA2Qzk1Mg=
As mentioned by the colleague in the comments, strict_encode64 is an option, as it complies to RFC 2045.
By the way, I got this insight from here: https://bugs.ruby-lang.org/issues/14664
Hope it helps! :)
First of all, please make sure that you are using the latest version of the SDK (2.2.2.2) from here
So, As I understand while we generate the presigned URL, we have to specify the SSECustomerMethod and when consuming the URL, the "x-amz-server-side-encryption-customer-key" header is set with the customer key, you also need to set the "x-amz-server-side-encryption-customer-algorithm" header.
var getPresignedUrlRequest = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = "EncryptedObject",
SSECustomerMethod= SSECustomerMethod.AES256,
Expires = DateTime.Now.AddMinutes(5)
};
var url = AWSClients.S3.GetPreSignedURL(getPresignedUrlRequest);
var webRequest = HttpWebRequest.Create(url);
webRequest.Headers.Add("x-amz-server-side-encryption-customer-algorithm", "AES256");
webRequest.Headers.Add("x-amz-server-side-encryption-customer-key", base64Key);
using (var response = webRequest.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var contents = reader.ReadToEnd();
}
How do I change the IP address of the DNS server?
In situation, I set Google DNS server in Windows Network Settins.
And I use LookupTXT function in Golang for getting DNS txt request.
But LookupTXT parameter is just the query string.
Any help or pointers would be highly appreciated. Thanks!
This is not straigtforward to do using golang at this point. You can however use a third party DNS package that allows configuring the resolver. First install the package:
go get github.com/bogdanovich/dns_resolver
Here is an example using it and the google resolvers 8.8.8.8 and 8.8.4.4:
package main
import (
"log"
"github.com/bogdanovich/dns_resolver"
)
func main() {
resolver := dns_resolver.New([]string{"8.8.8.8", "8.8.4.4"})
// In case of i/o timeout
resolver.RetryTimes = 5
ip, err := resolver.LookupHost("google.com")
if err != nil {
log.Fatal(err.Error())
}
log.Println(ip)
// Output [216.58.192.46]
}
Source
There is an open issue in golang here, so hopefully it becomes easier to do it with the builtin net package: https://github.com/golang/go/issues/12503. It could just be a documentation problem, as it is possible now, I just can't find an example.
EDIT: actually that package only supports lookupHost: https://github.com/bogdanovich/dns_resolver/blob/master/dns_resolver.go#L51-L79
So a PR would be required to add a TXT resolver.
2nd Edit: I made a PR with txt lookup here. That project hasn't been touched in years though so it may never get accepted.
System environment : Windows 7(64bit), VS 2013, Compile in release 64bit
This is the code:
var clients = new ZSocket(ctx, ZSocketType.ROUTER);
clients.GetOptionString(ZSocketOption.TCP_KEEPALIVE));
Exception as below:
ZeroMQ.ZException ""EINVAL(22): Invalid argument""
EINVAL ( Error: 22 ) was thrown correctly:
EINVAL
The requested option option_name is unknown, or the requested option_len or option_value is invalid, or the size of the buffer pointed to by option_value, as specified by option_len, is insufficient for storing the option value.
Must fix both of the below listed API non-compliant issues:
The ZeroMQ API defines the ZMQ_TCP_KEEPALIVE socket-configuration option to be of an int data type, not a string. Use the appropriate clrzmq4 binding interface function to request an int typed option_value and this first API incompliance will be solved.
Option value type int
Option value unit { -1 | 0 | 1 }
Default value -1 ( leave to OS default )
Applicable socket types all, when using TCP transports.
The ZeroMQ API defines this option to be valid if and only if the socket-instance was equipped with a tcp:// transport-class ( which the clients instance, as posted above, was not ). May defer the request to take place after the socket-instance has been instructed to associate a first tcp:// transport-class engine ( be it via a respective use of a .connect() or a .bind() ).
I've registered a record using the Bonjour API. Now I want to know the contents of the record I just published. I created it by specifying a NULL hostname, meaning, "use the daemon's default", but I can't find a simple way to query what that is!
With avahi, it's easy: I call avahi_client_get_host_name() to get the starting value of the machine's hostname.
For both avahi and Bonjour, the value of the SRV record can change during the lifetime of the registration - if the registration was done with a NULL hostname, the record's hostname is updated automatically when necessary. All I want here is a way to get the initial value of the hostname, at the time when I perform the registration.
Note that on my Snow Leopard test machine, the default multicast hostname is not the same as the machine's name from gethostname(2).
Four solutions I can think of:
Grab hostname in my process. It may be in there somewhere. I did a strings(3) search on a memory dump of my process, and found four instances of the multicast hostname in my address space, but that could be coincidence given the name is used for other things. Even if the string I'm after is in my process somewhere, I can't find an API to retrieve it sanely.
Query the hostname from the daemon. There may be some query I can send over the mach port to the daemon that fetches it? I can't find an API again. The relevant chunk of code is in the uDNS.c file in mDNSResponder, and doesn't seem to be exposed via the RPC interface.
I could just lookup the service I registered. This may involve a bit of network traffic though, so unless there's some guarantee that won't happen, I'm loathe to do it.
Re-implement the logic in uDNS.c. It grabs the machine's hostname from a combination of:
Dynamic DNS configuration
Statically configured multicast hostname
Reverse lookup of the primary interface's IPv4 address
It specifically doesn't use gethostname(2) or equivalent
Re-implementing that logic seems infeasible.
At the moment, I'm tending towards doing a lookup to grab the value of the initial SRV registration, but it doesn't seem ideal. What's the correct solution?
I needed to do exactly this. You want to use the ConvertDomainNameToCString macro (included in mDNSEmbeddedAPI.h), and you need access to the core mDNS structure.
Here's how you get the exact Bonjour/Zeroconf hostname that was registered:
char szHostname[512];
extern mDNS m;
ConvertDomainNameToCString(&m.MulticastHostname, szHostname);
I hope this helps you.
For the record, I went with (4), grabbing the machine's configuration to pull together the hostname the daemon is using without having to query it.
static char* getBonjourDefaultHost()
{
char* rv = 0;
#ifdef __APPLE__
CFStringRef name = SCDynamicStoreCopyLocalHostName(NULL);
if (name) {
int len = CFStringGetLength(name);
rv = new char[len*4+1];
CFStringGetCString(name, rv, len*4+1, kCFStringEncodingUTF8);
CFRelease(name);
}
// This fallback is completely incorrect, but why should we care...
// Mac does something crazy like <sysctl hw.model>-<MAC address>.
if (!rv)
rv = GetHostname(); // using gethostname(2)
#elif defined(WIN32)
CHAR tmp[256+1];
ULONG namelength = sizeof(tmp);
DynamicFn<BOOL (WINAPI*)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD)>
GetComputerNameExA_("Kernel32", "GetComputerNameExA");
if (!GetComputerNameExA_.isValid() ||
!(*GetComputerNameExA_)(ComputerNamePhysicalDnsHostname, tmp, &namelength))
tmp[0] = 0;
// Roughly correct; there's some obscure string cleaning mDNSResponder does
// to tidy up funny international strings.
rv = tmp[0] ? strdup(tmp) : strdup("My Computer");
#elif defined(__sun)
// This is exactly correct! What a relief.
rv = GetHostName();
#else
#error Must add platform mDNS daemon scheme
#endif
return rv;
}
From the command line one can obtain the local (Bonjour) hostname using the scutil command:
scutil --get LocalHostName
Programmatically this is obtainable using the kSCPropNetLocalHostName key via the SystemConfiguration Framework.
I know this is already answered, but I went looking to see how to do it with the SystemConfiguration framework based on what Pierz said in the second half of his answer.
This is what I got working, figured it might save someone else googling this some time:
In Swift:
import SystemConfiguration
let store = SCDynamicStoreCreate(nil, "ipmenu" as CFString, nil, nil )
if let hostNames = SCDynamicStoreCopyValue(store, "Setup:/Network/HostNames" as CFString){
if let hostName:String = (hostNames[kSCPropNetLocalHostName]) as? String {
print("Host name:\(hostName)")
}
}
I am building an SNMP Agent for a Windows application using the Microsoft WinSNMP API. Currently everything is working for single-item get and set-request, and also for get-next to allow walking the defined tree (albeit with some caveats that are not relevant to this question).
I am now looking at multi-item get and also get-bulk.
My current procedure is to iterate through the list of requested items (the varbindlist within the PDU), treating each one individually, effectively causing an internal get. The result is added to the VBL, set into the PDU, and then sent back to the SNMP Manager, taking into account invalid requests, etc.
My question is how should I handle "too much" data (data that cannot fit into a single transport layer message)? Or more accurately, is there a way to test whether data is "too big" without actually attempting to transmit? The only way I can see in the API is to try sending, check the error, and try again.
In the case of a get-request this isn't a problem - if you can't return all of the requested data, you fail: so attempt sending, and if the error report is SNMPAPI_TL_PDU_TOO_BIG, send a default "error" PDU.
However, it is allowable for a response to bulk-get to return partial results.
The only way I can see to handle this is a tedious (?) loop of removing an item and trying again. Something similar to the following (some detail removed for brevity):
// Create an empty varbindlist
vbl = SnmpCreateVbl(session, NULL, NULL);
// Add all items to the list
SnmpSetVb(vbl, &oid, &value); // for each OID/Value pair
// Create the PDU
pdu = SnmpCreatePdu(session, SNMP_PDU_RESPONSE, ..., vbl);
bool retry;
do {
retry = false;
smiINT failed = SnmpSendMsg(session, ..., pdu);
if (failed && SNMPAPI_TL_PDU_TOO_BIG == SnmpGetLastError()) {
// too much data, delete the last vb
SnmpDeleteVb(vbl, SnmpCountVbl(vbl));
SnmpSetPduData(pdu, ..., vbl);
retry = true;
};
} while(retry);
This doesn't seem like an optimal approach - so is there another way that I've missed?
As a side-note, I know about libraries such as net-snmp, but my question is specific to the Microsoft API.
The RFC does require you to do what you pasted,
https://www.rfc-editor.org/rfc/rfc3416
Read page 16.
There does not seem to be a function exposed by WinSNMP API that can do this for you, so you have to write your own logic to handle it.